Files
boursecu_user/src/views/base/home/components/PortfolioOverview.vue
T

314 lines
7.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.
<template>
<section :class="rootClass">
<header class="portfolio-overview__header">
<span class="portfolio-overview__eyebrow">کل دارایی شما</span>
<p class="portfolio-overview__total">
{{ usePriceWithoutType(totalRial) }}
<span class="portfolio-overview__unit">ریال</span>
</p>
</header>
<div
v-if="hasProfitInsight"
class="portfolio-overview__profit"
:class="{
'portfolio-overview__profit--up': profitTone === 'profit',
'portfolio-overview__profit--down': profitTone === 'loss',
}"
>
<div class="portfolio-overview__profit-main">
<span class="portfolio-overview__profit-amount">{{ profitText }}</span>
<span v-if="profitPercent" class="portfolio-overview__profit-percent">{{ profitPercent }}</span>
</div>
<div class="portfolio-overview__profit-label">
<span class="portfolio-overview__profit-hint">سود/زیان تحقق‌نیافته طلا</span>
<button
type="button"
class="portfolio-overview__profit-info"
aria-label="اطلاعات بیشتر"
@click="openProfitInfo"
>
<BasicIcon icon="vuesax-linear:info-circle" size="14" color="tertiary" />
</button>
</div>
</div>
<div class="portfolio-overview__grid">
<article class="portfolio-overview__tile">
<span class="portfolio-overview__tile-label">ارزش طلا</span>
<strong class="portfolio-overview__tile-value">{{ usePriceWithoutType(metalRial) }} ریال</strong>
<span v-if="goldWeight > 0" class="portfolio-overview__tile-sub">
{{ formatWeightAmount(goldWeight) }}{{ weightUnit }}
</span>
</article>
<article class="portfolio-overview__tile">
<span class="portfolio-overview__tile-label">موجودی نقد</span>
<strong class="portfolio-overview__tile-value">{{ usePriceWithoutType(cashRial) }} ریال</strong>
<span class="portfolio-overview__tile-sub">کیف پول ریالی</span>
</article>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, h } from 'vue'
import { Modal } from 'ant-design-vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { useWalletAssets } from '@/composables/useWalletAssets'
import {
formatPortfolioProfitInfoText,
formatPortfolioProfitLabel,
formatPortfolioProfitPercent,
formatPortfolioProfitText,
resolveAssetsRialValue,
resolveBoursecuCopperUnit,
} from '@/utils/productAssets'
import { formatWeightAmount, usePriceWithoutType } from '@/utils/usePrice'
import { getTradeBuyPrice, lastPrice } from '@/utils/price'
const props = withDefaults(
defineProps<{
variant?: 'mobile' | 'desktop'
}>(),
{ variant: 'mobile' }
)
const { assetsSummary } = useWalletAssets()
const portfolio = computed(() => assetsSummary.value?.portfolio)
const weightUnit = computed(() => resolveBoursecuCopperUnit(assetsSummary.value?.meta?.weight_unit))
const totalRial = computed(() =>
Number(portfolio.value?.total_rial_value ?? assetsSummary.value?.total_rial_value ?? 0)
)
const cashRial = computed(() =>
Number(portfolio.value?.cash_rial ?? assetsSummary.value?.cash_wallet ?? 0)
)
const metalRial = computed(() => {
const fromPortfolio = Number(portfolio.value?.metal_rial_value ?? 0)
if (fromPortfolio > 0) return fromPortfolio
return resolveAssetsRialValue(assetsSummary.value, getTradeBuyPrice(lastPrice.value))
})
const goldWeight = computed(() => Number(portfolio.value?.gold_weight ?? 0))
const hasProfitInsight = computed(() => portfolio.value?.unrealized_profit_rial != null)
const profitTone = computed(() => formatPortfolioProfitLabel(portfolio.value?.unrealized_profit_rial))
const profitText = computed(() => formatPortfolioProfitText(portfolio.value?.unrealized_profit_rial))
const profitPercent = computed(() =>
formatPortfolioProfitPercent(portfolio.value?.unrealized_profit_percent)
)
function renderModalLines(text: string) {
return h(
'div',
{ style: { lineHeight: '1.9' } },
text.split('\n').map((line, index) =>
h(
'div',
{ key: index, style: index > 0 ? { marginTop: '6px' } : undefined },
line
)
)
)
}
function openProfitInfo() {
const p = portfolio.value
if (!p) return
Modal.confirm({
title: 'سود/زیان تحقق‌نیافته طلا',
content: renderModalLines(formatPortfolioProfitInfoText(p, weightUnit.value)),
okText: 'متوجه شدم',
centered: true,
cancelButtonProps: { style: { display: 'none' } },
})
}
const rootClass = computed(() => [
'portfolio-overview',
`portfolio-overview--${props.variant}`,
])
</script>
<style scoped lang="less">
.portfolio-overview {
border-radius: 14px;
padding: 14px 16px;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.06));
border: 1px solid rgba(255, 255, 255, 0.22);
color: #fff;
}
.portfolio-overview--desktop {
margin-bottom: 16px;
padding: 20px 22px;
background: #fff;
border: 1px solid rgba(15, 23, 42, 0.06);
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
color: #111827;
}
.portfolio-overview__header {
margin-bottom: 12px;
}
.portfolio-overview__eyebrow {
display: block;
font-size: 12px;
font-weight: 600;
opacity: 0.85;
}
.portfolio-overview--desktop .portfolio-overview__eyebrow {
color: #6b7280;
opacity: 1;
}
.portfolio-overview__total {
margin: 4px 0 0;
font-size: 2rem;
// font-weight: 800;
line-height: 1.2;
letter-spacing: -0.02em;
}
.portfolio-overview__unit {
font-size: 0.85rem;
font-weight: 600;
margin-right: 4px;
opacity: 0.9;
}
.portfolio-overview__profit {
display: flex;
flex-direction: column;
gap: 6px;
padding: 10px 12px;
border-radius: 12px;
margin-bottom: 12px;
background: rgba(255, 255, 255, 0.12);
font-size: 13px;
font-weight: 700;
}
.portfolio-overview__profit-main {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.portfolio-overview__profit-label {
display: inline-flex;
align-items: center;
gap: 4px;
}
.portfolio-overview__profit-info {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
margin: 0;
border: none;
background: transparent;
cursor: pointer;
line-height: 0;
opacity: 0.85;
&:hover {
opacity: 1;
}
}
.portfolio-overview--desktop .portfolio-overview__profit {
background: #f9fafb;
}
.portfolio-overview__profit--up {
color: #bbf7d0;
}
.portfolio-overview--desktop .portfolio-overview__profit--up {
color: #027a48;
background: #ecfdf3;
}
.portfolio-overview__profit--down {
color: #fecaca;
}
.portfolio-overview--desktop .portfolio-overview__profit--down {
color: #b42318;
background: #fef3f2;
}
.portfolio-overview__profit-percent {
direction: ltr;
unicode-bidi: isolate;
font-variant-numeric: tabular-nums;
}
.portfolio-overview__profit-hint {
width: 100%;
font-size: 11px;
font-weight: 500;
opacity: 0.85;
}
.portfolio-overview--desktop .portfolio-overview__profit-hint {
color: #6b7280;
opacity: 1;
}
.portfolio-overview__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.portfolio-overview__tile {
padding: 10px 12px;
border-radius: 10px;
background: rgba(0, 0, 0, 0.12);
}
.portfolio-overview--desktop .portfolio-overview__tile {
background: #f6f7f9;
}
.portfolio-overview__tile-label {
display: block;
font-size: 11px;
font-weight: 600;
opacity: 0.85;
margin-bottom: 4px;
}
.portfolio-overview--desktop .portfolio-overview__tile-label {
color: #6b7280;
opacity: 1;
}
.portfolio-overview__tile-value {
display: block;
font-size: 14px;
font-weight: 700;
line-height: 1.35;
}
.portfolio-overview__tile-sub {
display: block;
margin-top: 4px;
font-size: 11px;
opacity: 0.8;
}
.portfolio-overview--desktop .portfolio-overview__tile-sub {
color: #9ca3af;
opacity: 1;
}
</style>