From d866fbe690c46510c3a1cb670a267db66c279c43 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 8 Dec 2025 19:10:10 +0500 Subject: [PATCH] . --- .../pages/marketing/MarketingAnalysis.vue | 374 ++++++++++++++++++ 1 file changed, 374 insertions(+) diff --git a/src/views/pages/marketing/MarketingAnalysis.vue b/src/views/pages/marketing/MarketingAnalysis.vue index 4dd741d..bf8c63e 100644 --- a/src/views/pages/marketing/MarketingAnalysis.vue +++ b/src/views/pages/marketing/MarketingAnalysis.vue @@ -226,6 +226,164 @@ + +
+ +
+
+

Сезонность спроса

+
+
+
+ +
+
+
+ + +
+
+

Воронка конверсии

+
+
+
+ +
+
+
+ + +
+
+

Потенциал каналов продвижения

+
+
+
+ +
+
+
+
+ {{ channel.name }} + +
+

{{ channel.justification }}

+
+
+
+
+ + +
+
+

Сегментация целевой аудитории

+
+
+
+
+

По возрасту

+
+ +
+
+
+

По полу

+
+ +
+
+
+
+
+ + +
+
+

Анализ конкурентов

+
+
+
+
+
+

{{ competitor.name }}

+
+
+ Охват: + {{ competitor.reach?.toLocaleString() || 0 }} +
+
+ Отзывы: + {{ competitor.reviews?.toLocaleString() || 0 }} +
+
+ Активность: + {{ competitor.activity }}% +
+
+
+

Сильные стороны:

+
    +
  • {{ strength }}
  • +
+
+
+

Слабые стороны:

+
    +
  • {{ weakness }}
  • +
+
+
+
+
+
+
+ + +
+
+

SWOT-анализ

+
+
+
+
+
+

Сильные стороны

+
    +
  • {{ strength }}
  • +
+
+
+
+
+

Слабые стороны

+
    +
  • {{ weakness }}
  • +
+
+
+
+
+

Возможности

+
    +
  • + {{ opportunity }} +
  • +
+
+
+
+
+

Угрозы

+
    +
  • {{ threat }}
  • +
+
+
+
+
+
+
+
@@ -275,6 +433,7 @@ import MarketingService from '@/service/MarketingService'; import { marked } from 'marked'; import Button from 'primevue/button'; +import Chart from 'primevue/chart'; import Checkbox from 'primevue/checkbox'; import InputText from 'primevue/inputtext'; import MultiSelect from 'primevue/multiselect'; @@ -684,6 +843,217 @@ const renderedSummary = computed(() => { return renderMarkdown(report.value.summary); }); +// Chart data computed properties +const seasonalityChartData = computed(() => { + if (!report.value?.chartsData?.seasonality) return null; + const seasonality = report.value.chartsData.seasonality; + const months = Object.keys(seasonality); + const values = Object.values(seasonality); + + return { + labels: months, + datasets: [ + { + label: 'Спрос (%)', + data: values, + borderColor: '#3B82F6', + backgroundColor: 'rgba(59, 130, 246, 0.1)', + tension: 0.4, + fill: true + } + ] + }; +}); + +const seasonalityChartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'top' + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + ticks: { + callback: function (value) { + return value + '%'; + } + } + } + } +}; + +const conversionFunnelChartData = computed(() => { + if (!report.value?.chartsData?.conversionFunnel || report.value.chartsData.conversionFunnel.length === 0) return null; + const funnel = report.value.chartsData.conversionFunnel; + + return { + labels: funnel.map((item) => item.stage), + datasets: [ + { + label: 'Значение', + data: funnel.map((item) => item.value), + backgroundColor: '#3B82F6', + borderColor: '#2563EB', + borderWidth: 1 + }, + { + label: 'Конверсия (%)', + data: funnel.map((item) => item.conversion), + backgroundColor: '#10B981', + borderColor: '#059669', + borderWidth: 1, + yAxisID: 'y1' + } + ] + }; +}); + +const conversionFunnelChartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'top' + } + }, + scales: { + y: { + beginAtZero: true, + position: 'left', + title: { + display: true, + text: 'Значение' + } + }, + y1: { + beginAtZero: true, + max: 100, + position: 'right', + title: { + display: true, + text: 'Конверсия (%)' + }, + grid: { + drawOnChartArea: false + }, + ticks: { + callback: function (value) { + return value + '%'; + } + } + } + } +}; + +const channelsPotentialChartData = computed(() => { + if (!report.value?.chartsData?.channelsPotential || report.value.chartsData.channelsPotential.length === 0) return null; + const channels = report.value.chartsData.channelsPotential; + + return { + labels: channels.map((ch) => ch.name), + datasets: [ + { + label: 'Потенциал', + data: channels.map((ch) => ch.potential), + backgroundColor: channels.map((ch) => { + if (ch.potential >= 80) return '#10B981'; + if (ch.potential >= 60) return '#F59E0B'; + return '#3B82F6'; + }), + borderColor: channels.map((ch) => { + if (ch.potential >= 80) return '#059669'; + if (ch.potential >= 60) return '#D97706'; + return '#2563EB'; + }), + borderWidth: 1 + } + ] + }; +}); + +const channelsPotentialChartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + max: 100, + ticks: { + callback: function (value) { + return value; + } + } + } + } +}; + +const ageGroupsChartData = computed(() => { + if (!report.value?.chartsData?.audienceSegmentation?.ageGroups || report.value.chartsData.audienceSegmentation.ageGroups.length === 0) return null; + const ageGroups = report.value.chartsData.audienceSegmentation.ageGroups; + + const colors = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899']; + + return { + labels: ageGroups.map((ag) => ag.label), + datasets: [ + { + data: ageGroups.map((ag) => parseFloat(ag.value)), + backgroundColor: ageGroups.map((_, idx) => colors[idx % colors.length]), + borderColor: '#fff', + borderWidth: 2 + } + ] + }; +}); + +const gendersChartData = computed(() => { + if (!report.value?.chartsData?.audienceSegmentation?.genders || report.value.chartsData.audienceSegmentation.genders.length === 0) return null; + const genders = report.value.chartsData.audienceSegmentation.genders; + + const colors = ['#EC4899', '#3B82F6']; + + return { + labels: genders.map((g) => g.label), + datasets: [ + { + data: genders.map((g) => parseFloat(g.value)), + backgroundColor: genders.map((_, idx) => colors[idx % colors.length]), + borderColor: '#fff', + borderWidth: 2 + } + ] + }; +}); + +const pieChartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'bottom' + }, + tooltip: { + callbacks: { + label: function (context) { + return context.label + ': ' + context.parsed + '%'; + } + } + } + } +}; + // Get analysis type label const getAnalysisTypeLabel = (type) => { const labels = { @@ -953,4 +1323,8 @@ onBeforeUnmount(() => { .markdown-content :deep(tr:hover) { background-color: var(--surface-50); } + +.chart-container { + position: relative; +}