130 lines
3.1 KiB
Vue
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>
|