118 lines
3.9 KiB
Vue
118 lines
3.9 KiB
Vue
<script setup>
|
||
import { useLayout } from '@/layout/composables/layout';
|
||
import { computed, onMounted, ref, watch } from 'vue';
|
||
|
||
const props = defineProps({
|
||
statusStats: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
loading: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
});
|
||
|
||
const { isDarkTheme } = useLayout();
|
||
|
||
const chartData = ref(null);
|
||
const chartOptions = ref(null);
|
||
|
||
const defaultColors = ['#10b981', '#3b82f6', '#f59e0b', '#8b5cf6', '#ec4899', '#06b6d4', '#64748b'];
|
||
|
||
function updateChart() {
|
||
const statuses = props.statusStats || [];
|
||
const labels = statuses.length > 0 ? statuses.map((s) => s.name) : ['نامشخص'];
|
||
const data = statuses.length > 0 ? statuses.map((s) => s.count) : [0];
|
||
const colors = statuses.map((s, i) => s.color || defaultColors[i % defaultColors.length]);
|
||
|
||
chartData.value = {
|
||
labels: labels,
|
||
datasets: [
|
||
{
|
||
data: data,
|
||
backgroundColor: colors,
|
||
borderWidth: 2,
|
||
borderColor: isDarkTheme.value ? '#18181b' : '#ffffff'
|
||
}
|
||
]
|
||
};
|
||
|
||
chartOptions.value = {
|
||
maintainAspectRatio: false,
|
||
responsive: true,
|
||
plugins: {
|
||
legend: {
|
||
position: 'bottom',
|
||
rtl: true,
|
||
labels: {
|
||
usePointStyle: true,
|
||
pointStyle: 'circle',
|
||
padding: 16,
|
||
font: {
|
||
family: 'Estedad',
|
||
size: 12
|
||
}
|
||
}
|
||
},
|
||
tooltip: {
|
||
rtl: true,
|
||
titleFont: { family: 'Estedad', size: 13 },
|
||
bodyFont: { family: 'Estedad', size: 12 },
|
||
callbacks: {
|
||
label: function (context) {
|
||
const count = context.parsed;
|
||
const statusItem = statuses[context.dataIndex];
|
||
const pct = statusItem?.percentage !== undefined ? ` (${statusItem.percentage}٪)` : '';
|
||
return `${context.label}: ${Number(count).toLocaleString('fa-IR')} ملک${pct}`;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
cutout: '65%'
|
||
};
|
||
}
|
||
|
||
watch(() => props.statusStats, () => {
|
||
updateChart();
|
||
}, { deep: true });
|
||
|
||
watch(isDarkTheme, () => {
|
||
updateChart();
|
||
});
|
||
|
||
onMounted(() => {
|
||
updateChart();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="card h-full p-5 border border-surface-200 dark:border-surface-800 rounded-2xl bg-surface-0 dark:bg-surface-900">
|
||
<div class="flex justify-between items-center mb-4">
|
||
<div>
|
||
<h3 class="font-bold text-lg text-surface-900 dark:text-surface-0 mb-1">وضعیت اسناد و املاک</h3>
|
||
<p class="text-xs text-muted-color">تفکیک املاک بر اساس وضعیت سند و مالکیت</p>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium bg-emerald-50 dark:bg-emerald-950/40 text-emerald-600 dark:text-emerald-400">
|
||
<i class="pi pi-check-circle text-[10px]"></i>
|
||
وضعیتها
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="loading" class="h-72 flex items-center justify-center">
|
||
<i class="pi pi-spin pi-spinner text-primary text-3xl"></i>
|
||
</div>
|
||
|
||
<div v-else-if="!statusStats || statusStats.length === 0" class="h-72 flex flex-col items-center justify-center text-muted-color">
|
||
<i class="pi pi-chart-pie text-4xl mb-2 opacity-40"></i>
|
||
<span class="text-sm">اطلاعات وضعیت املاک ثبت نشده است</span>
|
||
</div>
|
||
|
||
<div v-else class="h-72">
|
||
<Chart type="doughnut" :data="chartData" :options="chartOptions" class="h-full w-full" />
|
||
</div>
|
||
</div>
|
||
</template>
|