added pargar

This commit is contained in:
2026-07-05 20:49:01 +03:30
parent e4d92831c2
commit 51e3c3f0cb
7 changed files with 326 additions and 147 deletions
+3 -1
View File
@@ -39,11 +39,13 @@ export function getProductDisplayImage(product?: {
return resolveProductImageSrc(product)
}
import { formatGoldAmount } from '@/utils/usePrice'
export function formatMetalTotal(
metal: WalletMetalTotal,
inventoryCards: WalletInventoryCard[]
): string {
return `${resolveSummaryMetalWeight(metal, inventoryCards)}${metal.unit}`
return `${formatGoldAmount(resolveSummaryMetalWeight(metal, inventoryCards))}${metal.unit}`
}
export function resolveAssetsRialValue(
+115 -13
View File
@@ -6,6 +6,31 @@ export const serviceFeePercentage = ref<number>(0.01)
/** true = مبالغ رند: فروش → ریال دقیق (کارمزد از طلا)؛ خرید → وزن دقیق (کارمزد از ریال) */
export const feeDeductFromGold = ref(false)
/** آخرین فیلدی که کاربر مستقیم ویرایش کرده — مبنای به‌روزرسانی هنگام تغییر قیمت */
export type TradeInputLock = 'gram' | 'rial' | null
/** حداکثر اعشار ذخیره/ارسال به سرور (مطابق orders.amount) */
export const TRADE_GRAM_STORAGE_DECIMALS = 10
/** حداکثر اعشار نمایش به کاربر در اپ */
export const TRADE_GRAM_DISPLAY_DECIMALS = 8
export function normalizeOrderGoldAmount(grams?: number | null): number {
const value = Number(grams ?? 0)
if (!Number.isFinite(value) || value === 0) return 0
return parseFloat(value.toFixed(TRADE_GRAM_STORAGE_DECIMALS))
}
export function formatTradeGramDisplay(grams?: number | null): number {
const value = Number(grams ?? 0)
if (!Number.isFinite(value)) return 0
return parseFloat(value.toFixed(TRADE_GRAM_DISPLAY_DECIMALS))
}
function feeRate() {
return Number(serviceFeePercentage.value)
}
export async function loadServiceFeeFromSettings() {
try {
const settings = await getSettings()
@@ -39,7 +64,58 @@ export function calcFeeInGrams(goldValue?: number | null) {
/** گرم معادل برای دریافت دقیق مبلغ ریالی در فروش */
export function calcGoldAmountFromTargetRial(rialAmount: number, unitPriceRial: number) {
if (!rialAmount || !unitPriceRial) return 0
return parseFloat((rialAmount / unitPriceRial).toFixed(6))
return normalizeOrderGoldAmount(rialAmount / unitPriceRial)
}
/** وقتی کاربر گرم را قفل کرده — ریال نمایشی/قابل پرداخت از همان گرم */
export function calcRialFromLockedGram(displayGram: number, unitPriceRial: number, isBuy: boolean) {
const grams = Number(displayGram)
const unit = Number(unitPriceRial)
if (!grams || !unit) return 0
const fee = feeRate()
if (isBuy) {
if (feeDeductFromGold.value) {
return Math.round(grams * unit * (1 + fee))
}
return Math.round(grams * unit)
}
if (feeDeductFromGold.value) {
return Math.round(grams * unit)
}
return Math.round(grams * unit * (1 - fee))
}
/** وقتی کاربر ریال را قفل کرده — گرم نمایشی از همان مبلغ */
export function calcGramFromLockedRial(rialAmount: number, unitPriceRial: number, isBuy: boolean) {
const rial = Number(rialAmount)
const unit = Number(unitPriceRial)
if (!rial || !unit) return 0
const fee = feeRate()
if (isBuy) {
if (feeDeductFromGold.value) {
return rial / (unit * (1 + fee))
}
return (rial / unit) * (1 - fee)
}
if (feeDeductFromGold.value) {
return rial / unit
}
return rial / (unit * (1 - fee))
}
/** مبلغ ریالی قفل‌شده توسط کاربر — برای ارسال به سرور */
export function calcTradeTargetCash(
goldCash?: number | null,
inputLock?: TradeInputLock,
): number | undefined {
if (inputLock !== 'rial') return undefined
const cash = Number(goldCash ?? 0)
if (cash <= 0) return undefined
return Math.round(cash)
}
/** مبلغ نهایی قابل پرداخت (خرید) یا دریافت (فروش) به ریال */
@@ -49,41 +125,67 @@ export function calcPayablePriceRial(
isBuy: boolean,
targetRialCash?: number | null
) {
if (targetRialCash != null && Number(targetRialCash) > 0) {
return Number(targetRialCash)
}
if (feeDeductFromGold.value) {
if (isBuy) return finalPriceRial + feeInRial
if (targetRialCash != null && Number(targetRialCash) > 0) return Number(targetRialCash)
return finalPriceRial
}
return isBuy ? finalPriceRial : Math.max(0, finalPriceRial - feeInRial)
}
/** وزن ارسالی به سرور — فروش رند: از مبلغ ریالی هدف */
/** وزن ارسالی به سرور */
export function calcOrderGoldAmount(
goldValue?: number | null,
goldCash?: number | null,
unitPriceRial?: number,
isBuy?: boolean
isBuy?: boolean,
inputLock?: TradeInputLock
) {
const gold = Number(goldValue ?? 0)
if (isBuy || !feeDeductFromGold.value) {
return parseFloat(gold.toFixed(6))
}
const targetCash = calcSellTargetCash(goldCash, goldValue, unitPriceRial)
const cash = Number(goldCash ?? 0)
const unit = Number(unitPriceRial ?? 0)
if (targetCash != null && unit > 0) {
return calcGoldAmountFromTargetRial(targetCash, unit)
if (isBuy && inputLock === 'rial' && cash > 0 && unit > 0) {
if (feeDeductFromGold.value) {
const fee = feeRate()
return normalizeOrderGoldAmount(cash / (unit * (1 + fee)))
}
return normalizeOrderGoldAmount(cash / unit)
}
return parseFloat(gold.toFixed(6))
if (!isBuy && feeDeductFromGold.value) {
if (inputLock === 'rial' && cash > 0 && unit > 0) {
return calcGoldAmountFromTargetRial(cash, unit)
}
if (inputLock !== 'rial') {
return normalizeOrderGoldAmount(gold)
}
const targetCash = calcSellTargetCash(goldCash, goldValue, unitPriceRial, inputLock)
if (targetCash != null && unit > 0) {
return calcGoldAmountFromTargetRial(targetCash, unit)
}
}
return normalizeOrderGoldAmount(gold)
}
/** مبلغ ریالی دقیق دریافتی در فروش (فقط وقتی کاربر با ریال وارد کرده) */
export function calcSellTargetCash(
goldCash?: number | null,
goldValue?: number | null,
unitPriceRial?: number
unitPriceRial?: number,
inputLock?: TradeInputLock
) {
if (!feeDeductFromGold.value) return undefined
if (inputLock === 'gram') return undefined
const cash = Number(goldCash ?? 0)
if (!feeDeductFromGold.value || cash <= 0) return undefined
if (cash <= 0) return undefined
if (inputLock === 'rial') return Math.round(cash)
const gold = Number(goldValue ?? 0)
const unit = Number(unitPriceRial ?? 0)
if (gold > 0 && unit > 0) {
+88 -14
View File
@@ -1,24 +1,46 @@
export function usePrice(price: number, digit = true, unit = 'ریال') {
price= price??0
let _price = price.toString()
function formatGroupedPrice(price: string | number | null | undefined, digit: boolean) {
let value = price === null || price === undefined ? '' : String(price)
if (digit) {
_price = _price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
value = value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
return `${_price} ${unit}`
return value
}
export function usePriceWithoutType(price?: number, digit = true, unit = 'ریال') {
let _price = price?.toString()
if (digit) {
_price = _price?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
/** مبلغ ریالی — بدون اعشار، گرد به بالا */
export function ceilRial(price?: number | string | null): number {
const num = Number(price ?? 0)
if (!Number.isFinite(num)) {
return 0
}
return `${_price}`
return Math.ceil(num)
}
export function formatNumber(value: string | number | null | undefined, grouped = false): string {
return formatGroupedPrice(value, grouped)
}
export function usePrice(price: number, digit = true, unit = 'ریال') {
const formatted = formatGroupedPrice(ceilRial(price), digit)
return `${formatted} ${unit}`
}
export function usePriceWithoutType(price?: number, digit = true) {
return formatGroupedPrice(ceilRial(price), digit)
}
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
if (temp && temp.length > 0) return formatGroupedPrice(temp[0], false)
return formatGroupedPrice(Math.round(+num * 100) / 100, false)
}
export function formatSignedPercentChange(value: number): string {
if (!value) return '0%'
const formatted = useTowDecimal(Math.abs(value))
const prefix = value > 0 ? '+' : '-'
return `${prefix}${formatted}%`
}
export function useFormat(num: string | number, unit = '') {
@@ -28,11 +50,26 @@ export function useFormat(num: string | number, unit = '') {
type TransactionPriceSource = {
cash?: number | null
gold?: number | null
type?: number | null
order?: {
price?: number | null
total_price?: number | null
amount?: number | null
sts?: number | null
sts_str?: string | number | null
payment_sts?: number | null
} | null
withdraw?: {
sts?: number | null
sts_str?: string | number | null
} | null
order_id?: number | null
}
const ORDER_STATUS_LABELS: Record<number, string> = {
1: 'باز',
2: 'موفق',
3: 'ناموفق',
}
export function getTransactionTradePrice(item: TransactionPriceSource): number {
@@ -57,6 +94,38 @@ export function getTransactionTradePrice(item: TransactionPriceSource): number {
return 0
}
export function resolveTransactionStatusLabel(item?: TransactionPriceSource | null): string {
if (!item) return ''
const raw = item.order?.sts_str ?? item.withdraw?.sts_str
if (typeof raw === 'string') {
const trimmed = raw.trim()
if (trimmed && trimmed !== 'بدون مقدار') {
return trimmed
}
}
if (typeof raw === 'number' || (typeof raw === 'string' && /^\d+$/.test(String(raw).trim()))) {
const mapped = ORDER_STATUS_LABELS[Number(raw)]
if (mapped) return mapped
}
const sts = item.order?.sts ?? item.withdraw?.sts
if (sts != null && ORDER_STATUS_LABELS[Number(sts)]) {
return ORDER_STATUS_LABELS[Number(sts)]
}
if (item.order_id && Number(item.order?.payment_sts) === 4) {
return 'موفق'
}
if (item.order_id && (item.type === 1 || item.type === 2 || item.type === 10)) {
return 'موفق'
}
return ''
}
export function getTransactionGoldAmount(item: TransactionPriceSource): number {
const gold = Number(item.gold) || 0
if (Math.abs(gold) > 0) {
@@ -74,8 +143,13 @@ export function getTransactionGoldAmount(item: TransactionPriceSource): number {
return gold
}
export function formatGoldAmount(amount?: number | null): string {
export function formatGoldAmount(amount?: number | null, decimals = 8): string {
const num = Number(amount)
if (!Number.isFinite(num)) return '0'
return String(Number(num.toFixed(3)))
return formatGroupedPrice(Number(num.toFixed(decimals)), false)
}
/** وزن فلز (گرم) — بدون گرد کردن به بالا */
export function formatWeightAmount(amount?: number | null): string {
return formatGoldAmount(amount)
}