Files
CRM_Montzar/apps/web/src/components/dashboard/HomeSalesTrend.vue
T
2026-09-21 17:55:05 +03:30

130 lines
3.1 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue';
import type { HomeDashboardTrendPointDto } from '@mento/shared';
import UiChart, { type UiChartOption } from '@/components/ui/UiChart.vue';
const props = defineProps<{
trend: HomeDashboardTrendPointDto[];
changePercent: number | null;
formatChange: (value: number | null) => string;
formatAmount: (value: number) => string;
}>();
const trend = computed(() => props.trend);
const hasTrend = computed(() => trend.value.some((p) => p.amount > 0));
const change = computed(() => props.changePercent);
const option = computed<UiChartOption>(() => ({
animationDuration: 400,
grid: { top: 28, right: 8, bottom: 28, left: 8, containLabel: true },
tooltip: {
trigger: 'axis',
textStyle: { fontFamily: 'IRANSans, Tahoma, sans-serif' },
formatter: (params) => {
const row = Array.isArray(params) ? params[0] : params;
const point = trend.value[row.dataIndex ?? 0];
if (!point) return '';
return `${point.monthLabel}<br/>${props.formatAmount(point.amount)}`;
},
},
xAxis: {
type: 'category',
data: trend.value.map((p) => p.monthLabel),
boundaryGap: false,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
color: '#94a3b8',
fontFamily: 'IRANSans, Tahoma, sans-serif',
fontSize: 11,
},
},
yAxis: {
type: 'value',
min: 0,
...(hasTrend.value ? {} : { max: 1 }),
splitLine: { lineStyle: { color: '#eef2f7' } },
axisLabel: { show: false },
},
series: [
{
type: 'line',
data: trend.value.map((p) => p.amount),
smooth: 0.35,
symbol: 'circle',
symbolSize: 8,
itemStyle: { color: '#14b8a6', borderColor: '#fff', borderWidth: 2 },
lineStyle: { color: '#14b8a6', width: 3 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(20, 184, 166, 0.28)' },
{ offset: 1, color: 'rgba(20, 184, 166, 0)' },
],
},
},
},
],
}));
</script>
<template>
<article class="panel">
<header class="panel-head">
<h2>روند فروش</h2>
<span v-if="hasTrend && change != null" class="mini-badge" :class="{ down: change < 0 }">
{{ formatChange(change) }} نسبت به ماه قبل
</span>
</header>
<UiChart :option="option" height="210px" />
</article>
</template>
<style scoped>
.panel {
background: #fff;
border-radius: var(--radius);
padding: var(--pad-modal);
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
border: 1px solid var(--line);
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
margin-bottom: 0.35rem;
}
h2 {
margin: 0;
font-size: 0.95rem;
color: #0f172a;
}
.mini-badge {
font-size: 0.7rem;
font-weight: 700;
color: #16a34a;
background: #dcfce7;
border-radius: var(--radius);
padding: 0.18rem 0.5rem;
white-space: nowrap;
}
.mini-badge.down {
color: #dc2626;
background: #fee2e2;
}
</style>