Files
boursecu_user/src/components/GoldDayCalculatorModal.vue
T

238 lines
6.8 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<BasicModal
v-model:visible="visible"
width="400px"
destroy-on-close
custom-footer
wrap-class-name="app-premium-modal--scroll-tall"
>
<template #title>
<div class="app-premium-modal__title">
<BasicIcon icon="vuesax-linear:calculator" size="20" class="app-premium-modal__title-icon" />
<span>ماشین حساب</span>
</div>
</template>
<div class="gold-day-calc" dir="rtl">
<p class="gold-day-calc__hint text-xs text-gray-500">
وزن مورد نظر را وارد کنید تا ارزش آن بر اساس قیمت لحظه‌ای محاسبه شود.
</p>
<BasicInput
v-model:value="weight"
:title="`وزن ${metalTypeLabel} (${weightUnit})`"
type="tel"
place-holder="مثلاً ۱۰.۵"
inputmode="decimal"
/>
<BasicInput
:value="unitPriceInput"
:title="`قیمت روز ${metalTypeLabel}`"
type="tel"
disabled
addon-before="ریال"
place-holder="قیمت صفحه اصلی"
/>
<BasicInput
v-model:value="feePercent"
title="اجرت ساخت مفتول (%)"
type="tel"
place-holder="مثلاً ۷"
inputmode="decimal"
/>
<BasicInput
v-model:value="profitPercent"
title="سود مورد انتظار (%)"
type="tel"
place-holder="مثلاً ۵"
inputmode="decimal"
/>
<BasicInput
v-model:value="taxPercent"
title="مالیات ارزش افزوده (%)"
type="tel"
place-holder="مثلاً ۱۰"
inputmode="decimal"
/>
<div class="gold-day-calc__result rounded-xl border border-solid border-gray-200 bg-gray-50 p-3">
<div class="text-xs text-gray-500 mb-1">کل نهایی</div>
<div class="gold-day-calc__total text-lg font-bold text-primary">
<span class="gold-day-calc__num">{{ formattedTotal }}</span>
<span class="text-sm font-medium text-gray-600">ریال</span>
</div>
<div class="mt-2 flex flex-col gap-1 text-xs text-gray-600">
<div class="flex justify-between gap-2">
<span>هزینه {{ metalTypeLabel }}</span>
<span class="gold-day-calc__num">{{ formatRial(breakdown.goldCost) }}</span>
</div>
<div class="flex justify-between gap-2">
<span>اجرت ساخت مفتول</span>
<span class="gold-day-calc__num">{{ formatRial(breakdown.feeCost) }}</span>
</div>
<div class="flex justify-between gap-2">
<span>سود مورد انتظار</span>
<span class="gold-day-calc__num">{{ formatRial(breakdown.profitCost) }}</span>
</div>
<div class="flex justify-between gap-2">
<span>مالیات ارزش افزوده</span>
<span class="gold-day-calc__num">{{ formatRial(breakdown.taxCost) }}</span>
</div>
</div>
</div>
</div>
<template #footer>
<BasicButton type="primary" class="app-premium-modal__btn-primary !w-full" size="middle" @click="recalculate">
محاسبه مجدد
</BasicButton>
</template>
</BasicModal>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import BasicModal from '@/components/BasicPacks/BasicModal.vue'
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { goldDayCalculatorVisible } from '@/composables/useGoldDayCalculator'
import {
getLastPrices,
getTradeBuyPrice,
lastPrice,
startLastPricePolling,
stopLastPricePolling,
} from '@/utils/price'
import { getPriceDisplayState, NO_QUOTE_MESSAGE } from '@/utils/storeHours'
import { usePriceWithoutType } from '@/utils/usePrice'
import { persianToEnglishDigits } from '@/utils/persianDigits'
import { useSettingsStore } from '@/store/settingsStore'
const visible = goldDayCalculatorVisible
const settingsStore = useSettingsStore()
const metalTypeLabel = computed(() => settingsStore.metalTypeLabel)
const weightUnit = computed(() => settingsStore.tradeWeightUnit)
const weight = ref<string | number>('')
const feePercent = ref<string | number>('')
const profitPercent = ref<string | number>('')
const taxPercent = ref<string | number>('')
const tick = ref(0)
function parseNum(raw: string | number | undefined): number {
const s = persianToEnglishDigits(String(raw ?? ''))
.replace(/,/g, '')
.replace(/[^\d.]/g, '')
const n = Number.parseFloat(s)
return Number.isFinite(n) ? n : 0
}
const liveUnitPrice = computed(() => {
void tick.value
if (getPriceDisplayState() !== 'open') return 0
return getTradeBuyPrice(lastPrice.value) || 0
})
const unitPriceInput = computed(() => {
void tick.value
const state = getPriceDisplayState()
if (state === 'closed') return 'فروشگاه بسته است'
if (state === 'no_quote') return NO_QUOTE_MESSAGE
const p = liveUnitPrice.value
return p > 0 ? usePriceWithoutType(p) : ''
})
const breakdown = computed(() => {
void tick.value
const w = parseNum(weight.value)
const fee = parseNum(feePercent.value)
const profit = parseNum(profitPercent.value)
const tax = parseNum(taxPercent.value)
const unit = liveUnitPrice.value
const goldCost = w * unit
const feeCost = goldCost * (fee / 100)
const afterFee = goldCost + feeCost
const profitCost = afterFee * (profit / 100)
const taxCost = (afterFee + profitCost) * (tax / 100)
const total = afterFee + profitCost + taxCost
return { goldCost, feeCost, profitCost, taxCost, total }
})
const formattedTotal = computed(() => formatRial(breakdown.value.total))
function formatRial(n: number) {
if (!Number.isFinite(n) || n <= 0) return '۰'
return usePriceWithoutType(Math.round(n))
}
function recalculate() {
tick.value += 1
void getLastPrices()
}
watch(visible, async (open) => {
if (open) {
startLastPricePolling()
await getLastPrices()
tick.value += 1
} else {
stopLastPricePolling()
}
})
</script>
<style scoped>
.gold-day-calc {
display: flex;
flex-direction: column;
gap: 0.75rem;
direction: rtl;
text-align: right;
}
.gold-day-calc :deep(.ant-input-group) {
direction: ltr;
display: table;
width: 100%;
}
.gold-day-calc :deep(.ant-input-group > .ant-input) {
direction: ltr;
text-align: left;
}
.gold-day-calc :deep(.ant-input:not(.ant-input-group *)) {
text-align: right;
}
.gold-day-calc__hint {
margin: 0;
line-height: 1.7;
}
.gold-day-calc__result {
direction: rtl;
text-align: right;
}
.gold-day-calc__total {
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: flex-start;
gap: 0.35rem;
}
.gold-day-calc__num {
direction: ltr;
unicode-bidi: isolate;
display: inline-block;
}
</style>