@@ -1165,6 +1068,8 @@ function resetDashboard() {
const PPT_BLUE = '#5B89FF';
const PPT_ORANGE = '#FF8E4F';
const PPT_GREEN = '#52C41A';
+const PPT_GRADIENT_BLUE_START = '#5B89FF';
+const PPT_GRADIENT_BLUE_END = '#82A7FF';
const roiValue = computed(() => report.value?.sections?.marketing_economics?.metrics?.roi ?? '—');
const cacValue = computed(() => report.value?.sections?.marketing_economics?.metrics?.customer_acquisition_cost_cac ?? '—');
@@ -1386,10 +1291,12 @@ const compactBarOptions = computed(() => ({
const barWidth = Math.abs(bar.x - bar.base);
if (barWidth > 40) {
ctx.fillStyle = '#ffffff';
+ ctx.font = 'bold 12px Inter, system-ui, sans-serif';
ctx.textAlign = 'right';
- ctx.fillText(label, bar.x - 10, bar.y);
+ ctx.fillText(label, bar.x - 12, bar.y);
} else {
ctx.fillStyle = '#64748b';
+ ctx.font = 'bold 11px Inter, system-ui, sans-serif';
ctx.textAlign = 'left';
ctx.fillText(label, bar.x + 10, bar.y);
}
@@ -1398,10 +1305,12 @@ const compactBarOptions = computed(() => ({
ctx.textAlign = 'center';
if (barHeight > 25) {
ctx.fillStyle = '#ffffff';
- ctx.fillText(label, bar.x, bar.y + 14);
+ ctx.font = 'bold 14px Inter, system-ui, sans-serif';
+ ctx.fillText(label, bar.x, bar.y + 16);
} else {
ctx.fillStyle = '#64748b';
- ctx.fillText(label, bar.x, bar.y - 10);
+ ctx.font = 'bold 12px Inter, system-ui, sans-serif';
+ ctx.fillText(label, bar.x, bar.y - 12);
}
}
}
@@ -1509,8 +1418,8 @@ const ageBarData = computed(() => ({
backgroundColor: (context) => {
const { chart } = context;
const { ctx, chartArea } = chart;
- if (!chartArea) return 'rgba(91,137,255,0.55)';
- return makeVerticalGradient(ctx, chartArea, 'rgba(91,137,255,0.85)', 'rgba(91,137,255,0.25)');
+ if (!chartArea) return PPT_BLUE;
+ return makeVerticalGradient(ctx, chartArea, PPT_GRADIENT_BLUE_START, PPT_GRADIENT_BLUE_END);
}
}
]
@@ -1576,8 +1485,8 @@ const incomeBarData = computed(() => ({
backgroundColor: (context) => {
const { chart } = context;
const { ctx, chartArea } = chart;
- if (!chartArea) return 'rgba(82,196,26,0.55)';
- return makeVerticalGradient(ctx, chartArea, 'rgba(82,196,26,0.80)', 'rgba(82,196,26,0.25)');
+ if (!chartArea) return PPT_GREEN;
+ return makeVerticalGradient(ctx, chartArea, PPT_GREEN, '#73D13D');
}
}
]
@@ -1832,8 +1741,8 @@ const decisionFactorsBarData = computed(() => {
backgroundColor: (context) => {
const { chart } = context;
const { ctx, chartArea } = chart;
- if (!chartArea) return 'rgba(91,137,255,0.55)';
- return makeVerticalGradient(ctx, chartArea, 'rgba(91,137,255,0.85)', 'rgba(91,137,255,0.25)');
+ if (!chartArea) return PPT_BLUE;
+ return makeVerticalGradient(ctx, chartArea, PPT_GRADIENT_BLUE_START, PPT_GRADIENT_BLUE_END);
}
}
]
@@ -1866,8 +1775,8 @@ const refusalReasonsBarData = computed(() => {
backgroundColor: (context) => {
const { chart } = context;
const { ctx, chartArea } = chart;
- if (!chartArea) return 'rgba(255,142,79,0.55)';
- return makeVerticalGradient(ctx, chartArea, 'rgba(255,142,79,0.85)', 'rgba(255,142,79,0.25)');
+ if (!chartArea) return PPT_ORANGE;
+ return makeVerticalGradient(ctx, chartArea, PPT_ORANGE, '#FFBB96');
}
}
]
diff --git a/src/views/pages/marketing/MarketingAnalysis.vue b/src/views/pages/marketing/MarketingAnalysis.vue
index 4dc2a43..9846fbb 100644
--- a/src/views/pages/marketing/MarketingAnalysis.vue
+++ b/src/views/pages/marketing/MarketingAnalysis.vue
@@ -957,16 +957,16 @@ const drawBarValues = (chart) => {
// Горизонтальный: цифра справа темная
ctx.fillStyle = document.documentElement.classList.contains('dark') ? '#ffffff' : '#1f2937';
ctx.textAlign = 'left';
- ctx.fillText(label, bar.x + 8, bar.y);
+ ctx.fillText(label, bar.x + 8, bar.y);
} else {
// === ВЕРТИКАЛЬНЫЙ (КАК НА ФОТО) ===
const barHeight = Math.abs(bar.base - bar.y);
-
+
// Если столбец достаточно высокий (>25px), рисуем цифру ВНУТРИ белым цветом
if (barHeight > 25) {
- ctx.fillStyle = '#ffffff';
+ ctx.fillStyle = '#ffffff';
// bar.y - это верхушка столбца. Добавляем 15px, чтобы спустить цифру чуть ниже верха
- ctx.fillText(label, bar.x, bar.y + 15);
+ ctx.fillText(label, bar.x, bar.y + 15);
} else {
// Если столбец мелкий, рисуем цифру СВЕРХУ темным цветом
ctx.fillStyle = document.documentElement.classList.contains('dark') ? '#ffffff' : '#1f2937';
@@ -991,7 +991,7 @@ const photoStyleBarOptions = {
},
plugins: {
legend: { display: false }, // Легенда не нужна
- tooltip: {
+ tooltip: {
enabled: true,
callbacks: { label: (ctx) => `${ctx.formattedValue}%` }
}
@@ -1003,7 +1003,7 @@ const photoStyleBarOptions = {
grid: { display: true, drawBorder: false, color: 'rgba(0,0,0,0.05)' },
ticks: { font: { size: 10 } }
},
- x: {
+ x: {
grid: { display: false }, // Вертикальные линии убираем
ticks: {
font: { size: 12, weight: '600' },
@@ -1025,8 +1025,8 @@ const horizontalBarChartOptions = {
},
plugins: { legend: { display: false } },
scales: {
- x: { display: false, beginAtZero: true },
- y: {
+ x: { display: false, beginAtZero: true },
+ y: {
grid: { display: false },
ticks: { font: { size: 12, weight: '500' }, crossAlign: 'far' }
}
@@ -1058,8 +1058,8 @@ const conversionFunnelChartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: true, position: 'bottom' } },
- scales: {
- y: { beginAtZero: true, title: { display: true, text: 'Пользователи' } }
+ scales: {
+ y: { beginAtZero: true, title: { display: true, text: 'Пользователи' } }
}
};
@@ -1996,12 +1996,12 @@ const buildChannelsChartData = (acquisitionChannels) => {
if (!shares || typeof shares !== 'object') return null;
// Превращаем ключи "instagram_target" в "Instagram Target"
- const labels = Object.keys(shares).map(key =>
+ const labels = Object.keys(shares).map(key =>
key.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')
);
// Чистим значения от знака % для графика
- const values = Object.values(shares).map(val =>
+ const values = Object.values(shares).map(val =>
(typeof val === 'string') ? parseFloat(val.replace('%', '')) : val
);
@@ -2149,16 +2149,16 @@ const mapChartsDataToSegments = (chartsData) => {
segments.push({
type: 'chart',
key: 'ageStructure',
- title: 'АРЫСТАНБЕК',
+ title: 'Возрастная структура',
chartType: 'bar',
data: {
labels,
datasets: [{
data: values,
- // !!! ЦВЕТ КАК НА ФОТО (СИНИЙ) !!!
- backgroundColor: '#60a5fa',
+ // !!! ЦВЕТ КАК НА ФОТО (СИНИЙ) !!!
+ backgroundColor: '#60a5fa',
borderRadius: 8, // Скругление
- barPercentage: 0.65,
+ barPercentage: 0.65,
unit: '%'
}]
},
@@ -2177,7 +2177,7 @@ const mapChartsDataToSegments = (chartsData) => {
values = parsed.values;
} else {
labels = ['Низкий', 'Средний', 'Высокий'];
- values = [20, 50, 30];
+ values = [20, 50, 30];
}
segments.push({