157 lines
5.5 KiB
Vue
157 lines
5.5 KiB
Vue
<template>
|
|
<div class="customer-wallet-balance space-y-3">
|
|
<div v-if="loading" class="rounded-xl border border-gray-200 bg-gray-50 px-4 py-6 text-center text-sm text-gray-500">
|
|
در حال بارگذاری موجودی…
|
|
</div>
|
|
|
|
<div v-else-if="error" class="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
|
|
{{ error }}
|
|
</div>
|
|
|
|
<template v-else-if="summary">
|
|
<div
|
|
v-for="metal in summary.metal_totals"
|
|
:key="metal.metal_id"
|
|
class="overflow-hidden rounded-xl border border-gray-200"
|
|
>
|
|
<div class="flex items-center justify-between gap-3 bg-emerald-50 px-4 py-3">
|
|
<div class="flex min-w-0 items-center gap-2">
|
|
<BaseIcon icon="vuesax-linear:coin" size="18" color="#15803d" />
|
|
<span class="truncate font-700 text-emerald-900">{{ metal.label }}</span>
|
|
</div>
|
|
<span class="shrink-0 font-800 text-emerald-800">{{ formatMetalTotal(metal) }}</span>
|
|
</div>
|
|
|
|
<div v-if="linesForMetal(metal.metal_id).length" class="divide-y divide-gray-100 bg-white">
|
|
<div
|
|
v-for="line in linesForMetal(metal.metal_id)"
|
|
:key="line.key"
|
|
class="flex items-center justify-between gap-3 px-4 py-2.5 text-sm"
|
|
>
|
|
<span class="min-w-0 truncate text-gray-600">{{ line.label }}</span>
|
|
<span class="shrink-0 font-600 text-gray-900">{{ line.value }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="overflow-hidden rounded-xl border border-gray-200">
|
|
<div class="flex items-center justify-between gap-3 bg-sky-50 px-4 py-3">
|
|
<div class="flex items-center gap-2">
|
|
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#0369a1" />
|
|
<span class="font-700 text-sky-900">کل دارایی ریالی</span>
|
|
</div>
|
|
<span class="font-800 text-sky-800">{{ usePrice(summary.total_rial_value ?? 0, true, '') }}</span>
|
|
</div>
|
|
|
|
<div class="divide-y divide-gray-100 bg-white">
|
|
<div class="flex items-center justify-between gap-3 px-4 py-2.5 text-sm">
|
|
<span class="text-gray-600">کیف پول ریالی</span>
|
|
<span class="font-600">{{ usePrice(summary.cash_wallet ?? 0, true, '') }}</span>
|
|
</div>
|
|
<div
|
|
v-if="Number(summary.assets_rial_value ?? 0) > 0"
|
|
class="flex items-center justify-between gap-3 px-4 py-2.5 text-sm"
|
|
>
|
|
<span class="text-gray-600">ارزش ریالی فلزات</span>
|
|
<span class="font-600">{{ usePrice(summary.assets_rial_value ?? 0, true, '') }}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between gap-3 px-4 py-2.5 text-sm font-700">
|
|
<span>مجموع کل</span>
|
|
<span>{{ usePrice(summary.total_rial_value ?? 0, true, '') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch } from 'vue'
|
|
import { getCustomerAssetsSummary } from '@/api/customer'
|
|
import type { WalletAssetsSummary, WalletInventoryCard, WalletMetalTotal } from '@/model/walletAssets'
|
|
import { usePrice } from '@core/utils/usePrice'
|
|
import { formatMetalDisplayValue } from '@/utils/metalWeight'
|
|
|
|
const props = defineProps<{
|
|
customerId?: number | null
|
|
}>()
|
|
|
|
const loading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
const summary = ref<WalletAssetsSummary | null>(null)
|
|
|
|
watch(
|
|
() => props.customerId,
|
|
(id) => {
|
|
if (id) {
|
|
void load(id)
|
|
} else {
|
|
summary.value = null
|
|
error.value = null
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
async function load(customerId: number) {
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
summary.value = await getCustomerAssetsSummary(customerId)
|
|
} catch {
|
|
error.value = 'بارگذاری جزئیات موجودی ناموفق بود'
|
|
summary.value = null
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function formatMetalTotal(metal: WalletMetalTotal) {
|
|
return formatMetalDisplayValue(Number(metal.total_weight ?? 0), metal.metal_id)
|
|
}
|
|
|
|
function formatCard(card: WalletInventoryCard) {
|
|
const qty = Number(card.quantity ?? 0)
|
|
if (card.display_type === 'count') {
|
|
return `${qty.toLocaleString('fa-IR')} ${card.unit || 'عدد'}`
|
|
}
|
|
return `${qty.toLocaleString('fa-IR', { maximumFractionDigits: 3 })} ${card.unit || 'گرم'}`
|
|
}
|
|
|
|
function linesForMetal(metalId: number) {
|
|
const cards = summary.value?.inventory_cards ?? []
|
|
const countCards = summary.value?.count_products ?? []
|
|
const metal = summary.value?.metal_totals?.find((row) => row.metal_id === metalId)
|
|
|
|
const productLines = [...cards, ...countCards]
|
|
.filter((card) => Number(card.metal_id) === metalId && Number(card.quantity ?? 0) > 0)
|
|
.map((card) => ({
|
|
key: `card-${card.product_id}`,
|
|
label: card.title,
|
|
value: formatCard(card)
|
|
}))
|
|
|
|
const lines: Array<{ key: string; label: string; value: string }> = [...productLines]
|
|
|
|
if (metal && Number(metal.melted_weight ?? 0) > 0 && !productLines.some((line) => line.label.includes('آبشده'))) {
|
|
lines.push({
|
|
key: 'melted',
|
|
label: 'موجودی آبشده / کیف اصلی',
|
|
value: formatMetalDisplayValue(Number(metal.melted_weight), metalId)
|
|
})
|
|
}
|
|
|
|
if (metal && lines.length > 1) {
|
|
lines.push({
|
|
key: 'total',
|
|
label: 'مجموع',
|
|
value: formatMetalTotal(metal)
|
|
})
|
|
}
|
|
|
|
return lines
|
|
}
|
|
|
|
defineExpose({ reload: () => props.customerId && load(props.customerId) })
|
|
</script>
|