style: redesign with modern gradient UI and summary cards
This commit is contained in:
+294
-100
@@ -8,149 +8,268 @@
|
|||||||
<style>
|
<style>
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
||||||
background: #f5f7fa;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
color: #333;
|
min-height: 100vh;
|
||||||
padding: 20px;
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 1440px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 30px 24px;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 30px 0 20px;
|
padding: 40px 0 35px;
|
||||||
}
|
}
|
||||||
.header h1 {
|
.header h1 {
|
||||||
font-size: 24px;
|
font-size: 28px;
|
||||||
color: #1a1a2e;
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
.header p {
|
.header p {
|
||||||
color: #666;
|
color: rgba(255,255,255,0.8);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Summary Cards */
|
||||||
|
.summary-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.summary-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
.summary-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
.summary-card .label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #94a3b8;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.summary-card .value {
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e293b;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.summary-card .sub {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #64748b;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Controls */
|
||||||
.controls {
|
.controls {
|
||||||
max-width: 1400px;
|
background: #fff;
|
||||||
margin: 0 auto 20px;
|
border-radius: 16px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 20px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.control-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
.controls label {
|
.controls label {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #555;
|
color: #64748b;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.controls select {
|
.controls select {
|
||||||
padding: 6px 12px;
|
padding: 8px 14px;
|
||||||
border: 1px solid #ddd;
|
border: 1.5px solid #e2e8f0;
|
||||||
border-radius: 6px;
|
border-radius: 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
background: #fff;
|
background: #f8fafc;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
color: #334155;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
.controls select:focus {
|
||||||
|
border-color: #667eea;
|
||||||
|
box-shadow: 0 0 0 3px rgba(102,126,234,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Charts */
|
||||||
.charts-grid {
|
.charts-grid {
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto 30px;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
.chart-card {
|
.chart-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
.chart-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
|
||||||
}
|
}
|
||||||
.chart-card h3 {
|
.chart-card h3 {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #555;
|
color: #475569;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.chart-card h3::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 4px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||||
}
|
}
|
||||||
.chart-card canvas {
|
.chart-card canvas {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: 250px !important;
|
height: 240px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Table */
|
||||||
.table-section {
|
.table-section {
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
overflow-x: auto;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.table-section h2 {
|
.table-section h2 {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #1a1a2e;
|
color: #1e293b;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.table-section h2::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 4px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||||
|
}
|
||||||
|
.table-wrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
}
|
}
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
background: linear-gradient(135deg, #f8fafc, #f1f5f9);
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
background: #f0f2f5;
|
padding: 12px 14px;
|
||||||
padding: 10px 8px;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #555;
|
color: #475569;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
position: sticky;
|
font-size: 12px;
|
||||||
top: 0;
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
border-bottom: 2px solid #e2e8f0;
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
padding: 8px;
|
padding: 11px 14px;
|
||||||
border-bottom: 1px solid #f0f2f5;
|
border-bottom: 1px solid #f1f5f9;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
tr:hover td {
|
tr:hover td {
|
||||||
background: #f8f9fb;
|
background: #f8fafc;
|
||||||
}
|
}
|
||||||
.pkg-tag {
|
.pkg-tag {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 6px;
|
padding: 3px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 20px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
background: #e8f4fd;
|
font-weight: 500;
|
||||||
color: #1976d2;
|
background: linear-gradient(135deg, #eef2ff, #e0e7ff);
|
||||||
|
color: #4338ca;
|
||||||
|
border: 1px solid #c7d2fe;
|
||||||
}
|
}
|
||||||
.metric-up { color: #4caf50; }
|
.num-cell {
|
||||||
.metric-down { color: #f44336; }
|
font-variant-numeric: tabular-nums;
|
||||||
@media (max-width: 900px) {
|
font-family: 'SF Mono', 'Fira Code', monospace;
|
||||||
.charts-grid { grid-template-columns: 1fr; }
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.updated-time {
|
.updated-time {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #999;
|
color: rgba(255,255,255,0.7);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 20px;
|
margin-top: 24px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.charts-grid { grid-template-columns: 1fr; }
|
||||||
|
.summary-row { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.container { padding: 16px; }
|
||||||
|
.header h1 { font-size: 22px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.summary-row { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>Moboos 广告投放数据报告</h1>
|
<h1>Moboos 广告投放数据报告</h1>
|
||||||
<p>数据范围:每日自动更新</p>
|
<p>数据概览与趋势分析</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="summary-row" id="summaryRow"></div>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label>筛选广告主:</label>
|
<div class="control-group">
|
||||||
|
<label>广告主</label>
|
||||||
<select id="pkgFilter">
|
<select id="pkgFilter">
|
||||||
<option value="all">全部</option>
|
<option value="all">全部</option>
|
||||||
</select>
|
</select>
|
||||||
<label style="margin-left:12px;">指标:</label>
|
</div>
|
||||||
<select id="metricFilter">
|
<div class="control-group">
|
||||||
<option value="impressions">展示</option>
|
<label>日期范围</label>
|
||||||
<option value="clicks">点击</option>
|
<select id="dateFilter">
|
||||||
<option value="conversions">转化</option>
|
<option value="all">全部日期</option>
|
||||||
<option value="ecpm">eCPM</option>
|
|
||||||
<option value="cpc">CPC</option>
|
|
||||||
<option value="ctr">CTR</option>
|
|
||||||
<option value="ivr">IVR</option>
|
|
||||||
<option value="cvr">CVR</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="charts-grid">
|
<div class="charts-grid">
|
||||||
<div class="chart-card">
|
<div class="chart-card">
|
||||||
@@ -173,6 +292,7 @@
|
|||||||
|
|
||||||
<div class="table-section">
|
<div class="table-section">
|
||||||
<h2>明细数据</h2>
|
<h2>明细数据</h2>
|
||||||
|
<div class="table-wrapper">
|
||||||
<table id="dataTable">
|
<table id="dataTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -192,13 +312,15 @@
|
|||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="updated-time" id="updatedTime"></div>
|
<div class="updated-time" id="updatedTime"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const COLORS = [
|
const COLORS = [
|
||||||
'#1976d2','#e53935','#43a047','#fb8c00','#8e24aa',
|
'#667eea','#f56565','#48bb78','#ed8936','#9f7aea',
|
||||||
'#00acc1','#6d4c41','#546e7a','#d81b60','#00897b'
|
'#38b2ac','#e53e3e','#4299e1','#d69e2e','#ed64a6'
|
||||||
];
|
];
|
||||||
|
|
||||||
let rawData = [];
|
let rawData = [];
|
||||||
@@ -215,14 +337,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
populateFilter();
|
populateFilters();
|
||||||
renderAll();
|
renderAll();
|
||||||
document.getElementById('pkgFilter').addEventListener('change', renderAll);
|
document.getElementById('pkgFilter').addEventListener('change', renderAll);
|
||||||
document.getElementById('metricFilter').addEventListener('change', renderAll);
|
document.getElementById('dateFilter').addEventListener('change', renderAll);
|
||||||
document.getElementById('updatedTime').textContent = '数据更新时间:' + new Date().toLocaleString('zh-CN');
|
document.getElementById('updatedTime').textContent = '最后刷新:' + new Date().toLocaleString('zh-CN');
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateFilter() {
|
function populateFilters() {
|
||||||
const pkgs = [...new Set(rawData.map(d => d.pkg))];
|
const pkgs = [...new Set(rawData.map(d => d.pkg))];
|
||||||
const sel = document.getElementById('pkgFilter');
|
const sel = document.getElementById('pkgFilter');
|
||||||
pkgs.forEach(p => {
|
pkgs.forEach(p => {
|
||||||
@@ -231,28 +353,79 @@
|
|||||||
opt.textContent = p.split('.').pop();
|
opt.textContent = p.split('.').pop();
|
||||||
sel.appendChild(opt);
|
sel.appendChild(opt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dates = [...new Set(rawData.map(d => d.date))].sort();
|
||||||
|
const dateSel = document.getElementById('dateFilter');
|
||||||
|
dates.forEach(d => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = d;
|
||||||
|
opt.textContent = d.slice(5);
|
||||||
|
dateSel.appendChild(opt);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFilteredData() {
|
function getFilteredData() {
|
||||||
|
let data = rawData;
|
||||||
const pkg = document.getElementById('pkgFilter').value;
|
const pkg = document.getElementById('pkgFilter').value;
|
||||||
if (pkg === 'all') return rawData;
|
const date = document.getElementById('dateFilter').value;
|
||||||
return rawData.filter(d => d.pkg === pkg);
|
if (pkg !== 'all') data = data.filter(d => d.pkg === pkg);
|
||||||
|
if (date !== 'all') data = data.filter(d => d.date === date);
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAll() {
|
function renderAll() {
|
||||||
|
const data = getFilteredData();
|
||||||
|
renderSummary(data);
|
||||||
renderCharts();
|
renderCharts();
|
||||||
renderTable();
|
renderTable(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSummary(data) {
|
||||||
|
const totalImp = data.reduce((s, d) => s + d.impressions, 0);
|
||||||
|
const totalClk = data.reduce((s, d) => s + d.clicks, 0);
|
||||||
|
const totalConv = data.reduce((s, d) => s + d.conversions, 0);
|
||||||
|
const avgEcpm = data.length ? (data.reduce((s, d) => s + d.ecpm, 0) / data.length) : 0;
|
||||||
|
const avgCtr = data.length ? (data.reduce((s, d) => s + d.ctr, 0) / data.length) : 0;
|
||||||
|
|
||||||
|
document.getElementById('summaryRow').innerHTML = `
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">总展示</div>
|
||||||
|
<div class="value">${totalImp.toLocaleString()}</div>
|
||||||
|
<div class="sub">${data.length} 条记录</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">总点击</div>
|
||||||
|
<div class="value">${totalClk.toLocaleString()}</div>
|
||||||
|
<div class="sub">整体 CTR ${(totalClk/Math.max(totalImp,1)*100).toFixed(2)}%</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">总转化</div>
|
||||||
|
<div class="value">${totalConv.toLocaleString()}</div>
|
||||||
|
<div class="sub">整体 CVR ${(totalConv/Math.max(totalClk,1)*100).toFixed(2)}%</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">平均 eCPM</div>
|
||||||
|
<div class="value">${avgEcpm.toFixed(3)}</div>
|
||||||
|
<div class="sub">均值</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">平均 CTR</div>
|
||||||
|
<div class="value">${(avgCtr*100).toFixed(2)}%</div>
|
||||||
|
<div class="sub">均值</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCharts() {
|
function renderCharts() {
|
||||||
const data = getFilteredData();
|
const pkg = document.getElementById('pkgFilter').value;
|
||||||
const dates = [...new Set(data.map(d => d.date))].sort();
|
const chartData = pkg === 'all' ? rawData : rawData.filter(d => d.pkg === pkg);
|
||||||
const pkgs = [...new Set(data.map(d => d.pkg))];
|
const dates = [...new Set(chartData.map(d => d.date))].sort();
|
||||||
|
const pkgs = [...new Set(chartData.map(d => d.pkg))];
|
||||||
|
|
||||||
renderChart('chartImpressions', dates, pkgs, data, 'impressions');
|
renderChart('chartImpressions', dates, pkgs, chartData, 'impressions');
|
||||||
renderChart('chartClicks', dates, pkgs, data, 'clicks');
|
renderChart('chartClicks', dates, pkgs, chartData, 'clicks');
|
||||||
renderChart('chartConversions', dates, pkgs, data, 'conversions');
|
renderChart('chartConversions', dates, pkgs, chartData, 'conversions');
|
||||||
renderChart('chartEcpm', dates, pkgs, data, 'ecpm');
|
renderChart('chartEcpm', dates, pkgs, chartData, 'ecpm');
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderChart(canvasId, dates, pkgs, data, metric) {
|
function renderChart(canvasId, dates, pkgs, data, metric) {
|
||||||
@@ -268,11 +441,14 @@
|
|||||||
label: pkg.split('.').pop(),
|
label: pkg.split('.').pop(),
|
||||||
data: values,
|
data: values,
|
||||||
borderColor: COLORS[i % COLORS.length],
|
borderColor: COLORS[i % COLORS.length],
|
||||||
backgroundColor: COLORS[i % COLORS.length] + '20',
|
backgroundColor: COLORS[i % COLORS.length] + '15',
|
||||||
borderWidth: 2,
|
borderWidth: 2.5,
|
||||||
pointRadius: 3,
|
pointRadius: 4,
|
||||||
tension: 0.3,
|
pointBackgroundColor: '#fff',
|
||||||
fill: false
|
pointBorderWidth: 2,
|
||||||
|
pointHoverRadius: 6,
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -283,34 +459,52 @@
|
|||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
interaction: { mode: 'index', intersect: false },
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 11 } } }
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
labels: { boxWidth: 10, padding: 15, font: { size: 11 }, usePointStyle: true }
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: 'rgba(30,41,59,0.95)',
|
||||||
|
titleFont: { size: 12 },
|
||||||
|
bodyFont: { size: 12 },
|
||||||
|
padding: 12,
|
||||||
|
cornerRadius: 8,
|
||||||
|
displayColors: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: { grid: { display: false } },
|
x: {
|
||||||
y: { beginAtZero: true, grid: { color: '#f0f0f0' } }
|
grid: { display: false },
|
||||||
|
ticks: { font: { size: 11 }, color: '#94a3b8' }
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
grid: { color: '#f1f5f9', drawBorder: false },
|
||||||
|
ticks: { font: { size: 11 }, color: '#94a3b8' }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTable() {
|
function renderTable(data) {
|
||||||
const data = getFilteredData();
|
|
||||||
const sorted = [...data].sort((a, b) => b.date.localeCompare(a.date) || a.pkg.localeCompare(b.pkg));
|
const sorted = [...data].sort((a, b) => b.date.localeCompare(a.date) || a.pkg.localeCompare(b.pkg));
|
||||||
const tbody = document.querySelector('#dataTable tbody');
|
const tbody = document.querySelector('#dataTable tbody');
|
||||||
tbody.innerHTML = sorted.map(d => `
|
tbody.innerHTML = sorted.map(d => `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${d.date}</td>
|
<td>${d.date.slice(5)}</td>
|
||||||
<td>${d.ad_unit}</td>
|
<td style="font-weight:500">${d.ad_unit}</td>
|
||||||
<td><span class="pkg-tag">${d.pkg}</span></td>
|
<td><span class="pkg-tag">${d.pkg.split('.').pop()}</span></td>
|
||||||
<td>${d.impressions.toLocaleString()}</td>
|
<td class="num-cell">${d.impressions.toLocaleString()}</td>
|
||||||
<td>${d.clicks.toLocaleString()}</td>
|
<td class="num-cell">${d.clicks.toLocaleString()}</td>
|
||||||
<td>${d.conversions}</td>
|
<td class="num-cell">${d.conversions}</td>
|
||||||
<td>${d.ecpm.toFixed(3)}</td>
|
<td class="num-cell">${d.ecpm.toFixed(3)}</td>
|
||||||
<td>${d.cpc.toFixed(3)}</td>
|
<td class="num-cell">${d.cpc.toFixed(3)}</td>
|
||||||
<td>${(d.ctr * 100).toFixed(2)}%</td>
|
<td class="num-cell">${(d.ctr * 100).toFixed(2)}%</td>
|
||||||
<td>${(d.ivr * 100).toFixed(3)}%</td>
|
<td class="num-cell">${(d.ivr * 100).toFixed(3)}%</td>
|
||||||
<td>${(d.cvr * 100).toFixed(2)}%</td>
|
<td class="num-cell">${(d.cvr * 100).toFixed(2)}%</td>
|
||||||
</tr>
|
</tr>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user