Preserve the local Ant UI work across views/components and add the new home dashboard chart panels before merging latest main.
84 lines
3.1 KiB
Vue
84 lines
3.1 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
import { Empty as AEmpty } from 'ant-design-vue';
|
|
import { toFaDigits } from '@/lib/jalali';
|
|
import type { HomeDashboardSliceDto } from '@mento/shared';
|
|
import UiChart, { CHART_PALETTE, type UiChartOption } from '@/components/ui/UiChart.vue';
|
|
|
|
const props = defineProps<{
|
|
reasons: HomeDashboardSliceDto[];
|
|
formatAmount: (value: number) => string;
|
|
}>();
|
|
|
|
const rows = computed(() => props.reasons);
|
|
const hasData = computed(() => rows.value.some((r) => r.count > 0));
|
|
const total = computed(() => rows.value.reduce((sum, row) => sum + row.count, 0));
|
|
|
|
function share(count: number) {
|
|
if (!total.value) return 0;
|
|
return Math.round((count / total.value) * 100);
|
|
}
|
|
|
|
const option = computed<UiChartOption>(() => ({
|
|
animationDuration: 400,
|
|
color: CHART_PALETTE,
|
|
tooltip: {
|
|
trigger: 'item',
|
|
textStyle: { fontFamily: 'IRANSans, Tahoma, sans-serif' },
|
|
formatter: (p) => {
|
|
const item = Array.isArray(p) ? p[0] : p;
|
|
const row = rows.value.find((r) => r.label === item.name);
|
|
const amount =
|
|
row?.amount != null ? `<br/>${props.formatAmount(row.amount)}` : '';
|
|
return `${item.name}<br/>${toFaDigits(Number(item.value ?? 0))} — ${toFaDigits(item.percent ?? 0)}٪${amount}`;
|
|
},
|
|
},
|
|
legend: { show: false },
|
|
series: [
|
|
{
|
|
type: 'pie',
|
|
radius: ['58%', '78%'],
|
|
center: ['50%', '50%'],
|
|
itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 2 },
|
|
label: { show: false },
|
|
data: rows.value.map((r) => ({ name: r.label, value: r.count })),
|
|
},
|
|
],
|
|
}));
|
|
</script>
|
|
|
|
<template>
|
|
<section class="bg-white rounded-2xl border border-slate-200/80 p-4 shadow-sm h-full flex flex-col">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<h3 class="font-bold text-slate-800 text-base">دلایل باخت</h3>
|
|
<span v-if="hasData" class="text-xs text-slate-400">۱۲ ماه اخیر</span>
|
|
</div>
|
|
<a-empty v-if="!hasData" description="معاملهٔ باختهای ثبت نشده" />
|
|
<template v-else>
|
|
<div class="relative w-36 h-36 mx-auto">
|
|
<UiChart :option="option" height="144px" />
|
|
<div class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
|
|
<span class="text-2xl font-black text-slate-800">{{ toFaDigits(total) }}</span>
|
|
<span class="text-[11px] text-slate-400">معامله باخته</span>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-2 mt-3">
|
|
<div
|
|
v-for="(item, index) in rows"
|
|
:key="item.label"
|
|
class="rounded-xl border border-slate-200/70 bg-slate-50 px-2.5 py-2"
|
|
>
|
|
<div class="flex items-center gap-1.5">
|
|
<span
|
|
class="w-2 h-2 rounded-full shrink-0"
|
|
:style="{ background: CHART_PALETTE[index % CHART_PALETTE.length] }"
|
|
></span>
|
|
<span class="text-[11px] text-slate-600 truncate">{{ item.label }}</span>
|
|
</div>
|
|
<div class="text-sm font-black text-slate-800 mt-1">{{ toFaDigits(share(item.count)) }}٪</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</section>
|
|
</template>
|