From 112f470cc377d5be517b0f1942fdd8e81699fcde Mon Sep 17 00:00:00 2001 From: arys Date: Thu, 22 Jan 2026 13:36:32 +0500 Subject: [PATCH] fix --- src/views/pages/MarketingAnalysis.vue | 165 ++++++++++++++------------ 1 file changed, 86 insertions(+), 79 deletions(-) diff --git a/src/views/pages/MarketingAnalysis.vue b/src/views/pages/MarketingAnalysis.vue index a586411..542fe95 100644 --- a/src/views/pages/MarketingAnalysis.vue +++ b/src/views/pages/MarketingAnalysis.vue @@ -301,7 +301,7 @@
-
Арыстанбек
+
Возрастная структура
-
-
-
-
Возраст
-
Доля
-
-
-
-
{{ row.label }}
-
{{ row.value }}%
-
-
-
-
@@ -338,20 +324,6 @@
-
-
-
-
Возраст
-
Доход
-
-
-
-
{{ row.label }}
-
{{ row.value }} ₸
-
-
-
-
@@ -414,20 +386,6 @@
-
-
-
-
Конкурент
-
Видимость
-
-
-
-
{{ row.label }}
-
{{ row.visibility ?? '—' }}
-
-
-
-
@@ -731,20 +689,6 @@
-
-
-
-
Канал
-
Доля
-
-
-
-
{{ row.label }}
-
{{ row.value }}%
-
-
-
-
@@ -1418,10 +1362,63 @@ const ageRows = computed(() => { const compactBarOptions = computed(() => ({ maintainAspectRatio: false, - plugins: { legend: { display: false } }, + plugins: { + legend: { display: false }, + barValues: { + id: 'barValues', + afterDatasetsDraw(chart) { + const { ctx, data } = chart; + ctx.save(); + ctx.font = 'bold 12px Inter, system-ui, sans-serif'; + ctx.textBaseline = 'middle'; + + data.datasets.forEach((dataset, i) => { + const meta = chart.getDatasetMeta(i); + if (!meta.hidden) { + meta.data.forEach((bar, index) => { + const value = dataset.data[index]; + if (value !== null && value !== undefined && value !== 0) { + const unit = dataset.unit || ''; + const label = value.toLocaleString() + unit; + const isHorizontal = chart.options.indexAxis === 'y'; + + if (isHorizontal) { + const barWidth = Math.abs(bar.x - bar.base); + if (barWidth > 40) { + ctx.fillStyle = '#ffffff'; + ctx.textAlign = 'right'; + ctx.fillText(label, bar.x - 10, bar.y); + } else { + ctx.fillStyle = '#64748b'; + ctx.textAlign = 'left'; + ctx.fillText(label, bar.x + 10, bar.y); + } + } else { + const barHeight = Math.abs(bar.base - bar.y); + ctx.textAlign = 'center'; + if (barHeight > 25) { + ctx.fillStyle = '#ffffff'; + ctx.fillText(label, bar.x, bar.y + 14); + } else { + ctx.fillStyle = '#64748b'; + ctx.fillText(label, bar.x, bar.y - 10); + } + } + } + }); + } + }); + ctx.restore(); + } + } + }, scales: { - x: { ticks: { color: '#64748B' }, grid: { display: false, drawBorder: false } }, - y: { ticks: { color: '#64748B' }, grid: { color: 'rgba(148,163,184,0.25)', drawBorder: false } } + x: { ticks: { color: '#64748B', font: { size: 11 } }, grid: { display: false, drawBorder: false } }, + y: { + ticks: { color: '#64748B', font: { size: 10 } }, + grid: { color: 'rgba(148,163,184,0.15)', drawBorder: false }, + beginAtZero: true + } } })); @@ -1507,6 +1504,7 @@ const ageBarData = computed(() => ({ datasets: [ { data: ageRows.value.map((r) => r.value), + unit: '%', borderRadius: 10, backgroundColor: (context) => { const { chart } = context; @@ -1521,7 +1519,7 @@ const ageBarData = computed(() => ({ // Income by age rows const incomeRows = computed(() => { const incomeByAge = report.value?.sections?.target_audience?.income_by_age ?? {}; - + // Маппинг ключей бэкенда на ключи фронтенда const backendKeyMap = { '18-24': { '18-24': 1.0 }, @@ -1573,6 +1571,7 @@ const incomeBarData = computed(() => ({ datasets: [ { data: incomeRows.value.map((r) => r.value), + unit: ' ₸', borderRadius: 10, backgroundColor: (context) => { const { chart } = context; @@ -1683,6 +1682,7 @@ const visibilityBarData = computed(() => { datasets: [ { data: values, + unit: '%', borderRadius: 12, borderSkipped: false, backgroundColor: (context) => { @@ -1697,15 +1697,17 @@ const visibilityBarData = computed(() => { }; }); -const visibilityBarOptions = computed(() => ({ - indexAxis: 'y', - maintainAspectRatio: false, - plugins: { legend: { display: false } }, - scales: { - x: { ticks: { color: '#64748B' }, grid: { color: 'rgba(148,163,184,0.25)', drawBorder: false } }, - y: { ticks: { color: '#64748B' }, grid: { display: false, drawBorder: false } } - } -})); +const visibilityBarOptions = computed(() => { + const base = compactBarOptions.value; + return { + ...base, + indexAxis: 'y', + scales: { + x: { ...base.scales.x, grid: { color: 'rgba(148,163,184,0.15)', drawBorder: false } }, + y: { ...base.scales.y, grid: { display: false, drawBorder: false } } + } + }; +}); const pptFunnelSteps = computed(() => { const fm = report.value?.sections?.customer_journey?.funnel_metrics ?? {}; @@ -1825,6 +1827,7 @@ const decisionFactorsBarData = computed(() => { datasets: [ { data: values, + unit: '%', borderRadius: 10, backgroundColor: (context) => { const { chart } = context; @@ -1858,6 +1861,7 @@ const refusalReasonsBarData = computed(() => { datasets: [ { data: values, + unit: '%', borderRadius: 10, backgroundColor: (context) => { const { chart } = context; @@ -1893,6 +1897,7 @@ const channelSharesBarData = computed(() => { datasets: [ { data: items.map((i) => percentToNumber(i.value)), + unit: '%', borderRadius: 12, borderSkipped: false, backgroundColor: (context) => { @@ -1917,15 +1922,17 @@ const channelSharesTableRows = computed(() => { ]; }); -const channelSharesBarOptions = computed(() => ({ - indexAxis: 'y', - maintainAspectRatio: false, - plugins: { legend: { display: false } }, - scales: { - x: { ticks: { color: '#64748B' }, grid: { color: 'rgba(148,163,184,0.25)', drawBorder: false } }, - y: { ticks: { color: '#64748B' }, grid: { display: false, drawBorder: false } } - } -})); +const channelSharesBarOptions = computed(() => { + const base = compactBarOptions.value; + return { + ...base, + indexAxis: 'y', + scales: { + x: { ...base.scales.x, grid: { color: 'rgba(148,163,184,0.15)', drawBorder: false } }, + y: { ...base.scales.y, grid: { display: false, drawBorder: false } } + } + }; +}); const channelEfficiencyRows = computed(() => { const eff = report.value?.sections?.acquisition_channels?.channel_efficiency ?? {};