Add revenue by channel — Общий/Расход/Прибыль/ROI + bar chart доход vs расход

This commit is contained in:
neo committed 2026-06-20 20:58:38 +05:00
1 parent d5fb5b4949
commit a825d84fee
1 file changed
+89 -16
+89 -16
View File
@@ -127,6 +127,8 @@ body{font-family:'Inter',-apple-system,sans-serif;background:var(--bg);color:var
<div class="sum-row" id="cpa-sum"></div> <div class="sum-row" id="cpa-sum"></div>
<div class="sum-row" id="rev-sum" style="margin-bottom:12px"></div>
<div class="cr cr2"> <div class="cr cr2">
<div class="cc"> <div class="cc">
<div class="ch"><div class="ct" id="cpa-main-t"></div><div class="cs" id="cpa-main-s"></div></div> <div class="ch"><div class="ct" id="cpa-main-t"></div><div class="cs" id="cpa-main-s"></div></div>
@@ -139,6 +141,11 @@ body{font-family:'Inter',-apple-system,sans-serif;background:var(--bg);color:var
</div> </div>
<div class="cc" style="margin-bottom:12px"> <div class="cc" style="margin-bottom:12px">
<div class="ch"><div class="ct">Доход по каналам</div><div class="cs">Выручка и расход по каналам привлечения</div></div>
<div class="cd s" id="rev-channel"></div>
</div>
<div class="cc" style="margin-bottom:12px">
<div class="ch"><div class="ct">Раскрытие: ключевые запросы</div><div class="cs">Нажмите на канал для детализации</div></div> <div class="ch"><div class="ct">Раскрытие: ключевые запросы</div><div class="cs">Нажмите на канал для детализации</div></div>
<div id="cpa-table-wrap" style="max-height:350px;overflow:auto;margin-top:12px"></div> <div id="cpa-table-wrap" style="max-height:350px;overflow:auto;margin-top:12px"></div>
</div> </div>
@@ -150,6 +157,8 @@ body{font-family:'Inter',-apple-system,sans-serif;background:var(--bg);color:var
<div class="sum-row" id="conv-sum"></div> <div class="sum-row" id="conv-sum"></div>
<div class="sum-row" id="conv-rev" style="margin-bottom:12px"></div>
<div class="cr cr2"> <div class="cr cr2">
<div class="cc"> <div class="cc">
<div class="ch"><div class="ct" id="cv-main-t"></div><div class="cs" id="cv-main-s"></div></div> <div class="ch"><div class="ct" id="cv-main-t"></div><div class="cs" id="cv-main-s"></div></div>
@@ -183,6 +192,8 @@ body{font-family:'Inter',-apple-system,sans-serif;background:var(--bg);color:var
<div class="sum-row" id="ltv-sum"></div> <div class="sum-row" id="ltv-sum"></div>
<div class="sum-row" id="ltv-rev" style="margin-bottom:12px"></div>
<div class="cr cr2"> <div class="cr cr2">
<div class="cc"> <div class="cc">
<div class="ch"><div class="ct" id="ltv-main-t"></div><div class="cs" id="ltv-main-s"></div></div> <div class="ch"><div class="ct" id="ltv-main-t"></div><div class="cs" id="ltv-main-s"></div></div>
@@ -227,11 +238,15 @@ const DATA = {
spend: {v:'1.1М ₽',d:'+8%',t:'up',n:'расход на рекламу'}, spend: {v:'1.1М ₽',d:'+8%',t:'up',n:'расход на рекламу'},
ratio: {v:'62%',d:'−3%',t:'down',n:'CPA / средний чек'}, ratio: {v:'62%',d:'−3%',t:'down',n:'CPA / средний чек'},
channels: [ channels: [
{n:'Яндекс Директ',spend:520000,conv:120,cpa:4333,avg:863}, {n:'Яндекс Директ',spend:520000,conv:120,cpa:4333,avg:863,revenue:1072000},
{n:'VK Реклама',spend:280000,conv:65,cpa:4308,avg:862}, {n:'VK Реклама',spend:280000,conv:65,cpa:4308,avg:862,revenue:560000},
{n:'SEO',spend:150000,conv:35,cpa:4286,avg:857}, {n:'SEO',spend:150000,conv:35,cpa:4286,avg:857,revenue:299857},
{n:'Ретаргетинг',spend:120000,conv:15,cpa:8000,avg:800}, {n:'Ретаргетинг',spend:120000,conv:15,cpa:8000,avg:800,revenue:120000},
], ],
totalRevenue: '2 051 857 ₽',
totalSpend: '1 070 000 ₽',
totalProfit: '981 857 ₽',
roi: '91.8%',
keywords: { keywords: {
'Яндекс Директ': [ 'Яндекс Директ': [
{q:'купить [товар] москва', clicks:3200, conv:85, cpa:3200, cost_per_click:45}, {q:'купить [товар] москва', clicks:3200, conv:85, cpa:3200, cost_per_click:45},
@@ -336,10 +351,14 @@ const DATA = {
spend: {v:'275К ₽',d:'+5%',t:'up',n:'расход на рекламу'}, spend: {v:'275К ₽',d:'+5%',t:'up',n:'расход на рекламу'},
ratio: {v:'18%',d:'−2%',t:'down',n:'CPA / средний чек'}, ratio: {v:'18%',d:'−2%',t:'down',n:'CPA / средний чек'},
channels: [ channels: [
{n:'Яндекс Директ',spend:140000,conv:42,cpa:3333,avg:18476}, {n:'Яндекс Директ',spend:140000,conv:42,cpa:3333,avg:18476,revenue:775952},
{n:'VK Реклама',spend:75000,conv:22,cpa:3409,avg:18409}, {n:'VK Реклама',spend:75000,conv:22,cpa:3409,avg:18409,revenue:405000},
{n:'SEO',spend:60000,conv:22,cpa:2727,avg:18182}, {n:'SEO',spend:60000,conv:22,cpa:2727,avg:18182,revenue:400004},
], ],
totalRevenue: '1 580 956 ₽',
totalSpend: '275 000 ₽',
totalProfit: '1 305 956 ₽',
roi: '474.9%',
keywords: { keywords: {
'Яндекс Директ': [ 'Яндекс Директ': [
{q:'создать сайт цена', clicks:1800, conv:42, cpa:2800, cost_per_click:52}, {q:'создать сайт цена', clicks:1800, conv:42, cpa:2800, cost_per_click:52},
@@ -424,11 +443,15 @@ const DATA = {
spend: {v:'4.2М ₽',d:'+4%',t:'up',n:'расход на рекламу'}, spend: {v:'4.2М ₽',d:'+4%',t:'up',n:'расход на рекламу'},
ratio: {v:'5.2%',d:'−1%',t:'down',n:'CPA / средний чек'}, ratio: {v:'5.2%',d:'−1%',t:'down',n:'CPA / средний чек'},
channels: [ channels: [
{n:'Яндекс Директ',spend:2100000,conv:150,cpa:14000,avg:270000}, {n:'Яндекс Директ',spend:2100000,conv:150,cpa:14000,avg:270000,revenue:4050000},
{n:'VK Реклама',spend:980000,conv:100,cpa:9800,avg:245000}, {n:'VK Реклама',spend:980000,conv:100,cpa:9800,avg:245000,revenue:2450000},
{n:'SEO',spend:520000,conv:50,cpa:10400,avg:260000}, {n:'SEO',spend:520000,conv:50,cpa:10400,avg:260000,revenue:1300000},
{n:'Выставки',spend:600000,conv:40,cpa:15000,avg:300000}, {n:'Выставки',spend:600000,conv:40,cpa:15000,avg:300000,revenue:1200000},
], ],
totalRevenue: '10 050 000 ₽',
totalSpend: '4 200 000 ₽',
totalProfit: '5 850 000 ₽',
roi: '139.3%',
keywords: { keywords: {
'Яндекс Директ': [ 'Яндекс Директ': [
{q:'купить [оборудование] оптом', clicks:2800, conv:55, cpa:11200, cost_per_click:48}, {q:'купить [оборудование] оптом', clicks:2800, conv:55, cpa:11200, cost_per_click:48},
@@ -553,6 +576,14 @@ function renderCPA(d) {
{l:'CPA / чек',v:c.ratio.v,d:c.ratio.d,t:c.ratio.t,n:c.ratio.n}, {l:'CPA / чек',v:c.ratio.v,d:c.ratio.d,t:c.ratio.t,n:c.ratio.n},
]); ]);
// Revenue summary
sumRow('rev-sum', [
{l:'Общий доход',v:c.totalRevenue,d:'+15%',t:'up',n:'за 30 дней'},
{l:'Расход',v:c.totalSpend,d:'+8%',t:'up',n:'на рекламу'},
{l:'Прибыль',v:c.totalProfit,d:'+12%',t:'up',n:'доход − расход'},
{l:'ROI',v:c.roi,d:'+5%',t:'up',n:'возврат инвестиций'},
]);
// Main: CPA trend // Main: CPA trend
const mc = document.getElementById('cpa-main'); const mc = document.getElementById('cpa-main');
if (!charts.cpaMain) charts.cpaMain = echarts.init(mc); if (!charts.cpaMain) charts.cpaMain = echarts.init(mc);
@@ -597,6 +628,30 @@ function renderCPA(d) {
// Table: Keywords // Table: Keywords
renderKeywordTable(c.keywords); renderKeywordTable(c.keywords);
// Revenue by channel
const rc = document.getElementById('rev-channel');
if (!charts.revChannel) charts.revChannel = echarts.init(rc);
const revData = c.channels.map(ch => ({
name: ch.n,
spend: ch.spend,
revenue: ch.revenue,
profit: ch.revenue - ch.spend,
roi: ((ch.revenue - ch.spend) / ch.spend * 100).toFixed(0) + '%',
}));
const chNames = revData.map(r => r.name);
charts.revChannel.setOption({
tooltip:{trigger:'axis',backgroundColor:'#111',borderColor:'#222',textStyle:{color:'#fafafa',fontSize:11}},
legend:{data:['Доход','Расход'],textStyle:{color:'#52525b',fontSize:10},top:0},
grid:{left:100,right:16,top:30,bottom:10},
xAxis:{type:'category',data:chNames,axisLine:{lineStyle:{color:'#1e1e22'}},axisLabel:{color:'#a1a1aa',fontSize:9}},
yAxis:{type:'value',splitLine:{lineStyle:{color:'#1a1a1e'}},axisLabel:{color:'#52525b',fontSize:10,formatter:v=>v>=1e6?(v/1e6).toFixed(0)+'М':(v/1e3).toFixed(0)+'К'}},
series:[
{name:'Доход',type:'bar',data:revData.map(r=>r.revenue),barWidth:'35%',itemStyle:{color:new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:'#22c55e'},{offset:1,color:'rgba(34,197,94,.3)'}]),borderRadius:[4,4,0,0]}},
{name:'Расход',type:'bar',data:revData.map(r=>r.spend),barWidth:'35%',itemStyle:{color:new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:'#3b82f6'},{offset:1,color:'rgba(59,130,246,.3)'}]),borderRadius:[4,4,0,0]}},
],
animationDuration:600,
},true);
} }
function renderKeywordTable(keywords) { function renderKeywordTable(keywords) {
@@ -627,11 +682,20 @@ function renderConv(d) {
const c = d.conv; const c = d.conv;
sumRow('conv-sum', [ sumRow('conv-sum', [
{l:'Конверсия',v:c.avg.v,d:c.avg.d,t:c.avg.t,n:c.avg.n}, {l:'Конверсия',v:c.avg.v,d:c.avg.d,t:c.avg.t,n:c.avg.n},
{l:'Лиды',v:c.leads.v,d:c.leads.d,t:c.leads.t,n:c.leads.n}, {l:'Лиды',v:c.leads.v,d:c.leads.d,t:c.leads.t,n:c.leads.n},
{l:'Продажи',v:c.sales.v,d:c.sales.d,t:c.sales.t,n:c.sales.n}, {l:'Продажи',v:c.sales.v,d:c.sales.d,t:c.sales.t,n:c.sales.n},
{l:'Цикл',v:c.cycle.v,d:c.cycle.d,t:c.cycle.t,n:c.cycle.n}, {l:'Цикл',v:c.cycle.v,d:c.cycle.d,t:c.cycle.t,n:c.cycle.n},
]); ]);
// Revenue in conv tab (using CPA data)
const rc = DATA[curSeg].cpa;
sumRow('conv-rev', [
{l:'Общий доход',v:rc.totalRevenue,d:'+15%',t:'up',n:'за 30 дней'},
{l:'Расход',v:rc.totalSpend,d:'+8%',t:'up',n:'на рекламу'},
{l:'Прибыль',v:rc.totalProfit,d:'+12%',t:'up',n:'доход − расход'},
{l:'ROI',v:rc.roi,d:'+5%',t:'up',n:'возврат инвестиций'},
]);
// Main: Conv trend // Main: Conv trend
const mc = document.getElementById('cv-main'); const mc = document.getElementById('cv-main');
@@ -717,6 +781,15 @@ function renderLTV(d) {
{l:'Ср. чек',v:c.avgCheck.v,d:c.avgCheck.d,t:c.avgCheck.t,n:c.avgCheck.n}, {l:'Ср. чек',v:c.avgCheck.v,d:c.avgCheck.d,t:c.avgCheck.t,n:c.avgCheck.n},
]); ]);
// Revenue in LTV tab
const rc = DATA[curSeg].cpa;
sumRow('ltv-rev', [
{l:'Общий доход',v:rc.totalRevenue,d:'+15%',t:'up',n:'за 30 дней'},
{l:'Расход',v:rc.totalSpend,d:'+8%',t:'up',n:'на рекламу'},
{l:'Прибыль',v:rc.totalProfit,d:'+12%',t:'up',n:'доход − расход'},
{l:'ROI',v:rc.roi,d:'+5%',t:'up',n:'возврат инвестиций'},
]);
// Main: LTV curve // Main: LTV curve
const mc = document.getElementById('ltv-main'); const mc = document.getElementById('ltv-main');
if (!charts.ltvMain) charts.ltvMain = echarts.init(mc); if (!charts.ltvMain) charts.ltvMain = echarts.init(mc);