feat: add Campaign ID detail module (campid -> pkg + geo + channel)

This commit is contained in:
王源
2026-09-07 16:10:59 +08:00
parent a2d29aa9e6
commit 1868a48163
2 changed files with 400 additions and 2 deletions
+330
View File
@@ -0,0 +1,330 @@
[
{
"campid": "91530535",
"product": "Honor of Kings",
"pkg": "com.levelinfinite.sgameGlobal",
"geo": "IN",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539877",
"product": "Honor of Kings",
"pkg": "com.levelinfinite.sgameGlobal",
"geo": "IN",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91530534",
"product": "Honor of Kings",
"pkg": "com.levelinfinite.sgameGlobal",
"geo": "PH",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539876",
"product": "Honor of Kings",
"pkg": "com.levelinfinite.sgameGlobal",
"geo": "PH",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91546637",
"product": "IGG (Lords Mobile)",
"pkg": "com.igg.android.lordsmobile",
"geo": "RU",
"channel": "MediaGo",
"mode": "自投"
},
{
"campid": "91530536",
"product": "IGG (Lords Mobile)",
"pkg": "com.igg.android.lordsmobile",
"geo": "RU",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539867",
"product": "IGG (Lords Mobile)",
"pkg": "com.igg.android.lordsmobile",
"geo": "RU",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91544868",
"product": "IGG (Lords Mobile)",
"pkg": "com.igg.android.lordsmobile",
"geo": "RU",
"channel": "传音",
"mode": "自投"
},
{
"campid": "91530547",
"product": "Magalu_BR (Android)",
"pkg": "com.luizalabs.mlapp",
"geo": "BR",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539871",
"product": "Magalu_BR (Android)",
"pkg": "com.luizalabs.mlapp",
"geo": "BR",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91530548",
"product": "Magalu_BR (iOS)",
"pkg": "id891130066",
"geo": "BR",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539872",
"product": "Magalu_BR (iOS)",
"pkg": "id891130066",
"geo": "BR",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91530549",
"product": "Netshoes_BR (Android)",
"pkg": "br.com.netshoes.app",
"geo": "BR",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539873",
"product": "Netshoes_BR (Android)",
"pkg": "br.com.netshoes.app",
"geo": "BR",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91530550",
"product": "Netshoes_BR (iOS)",
"pkg": "id886443637",
"geo": "BR",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539874",
"product": "Netshoes_BR (iOS)",
"pkg": "id886443637",
"geo": "BR",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91546638",
"product": "Shahid",
"pkg": "net.mbc.shahid",
"geo": "SA",
"channel": "MediaGo",
"mode": "自投"
},
{
"campid": "91530537",
"product": "Shahid",
"pkg": "net.mbc.shahid",
"geo": "SA",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539868",
"product": "Shahid",
"pkg": "net.mbc.shahid",
"geo": "SA",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91550107",
"product": "Shahid",
"pkg": "net.mbc.shahid",
"geo": "SA",
"channel": "Smartyads",
"mode": "自投"
},
{
"campid": "91544867",
"product": "Shahid",
"pkg": "net.mbc.shahid",
"geo": "SA",
"channel": "传音",
"mode": "自投"
},
{
"campid": "91530538",
"product": "Water Sort",
"pkg": "water.sort.color.puzzle.offline.games",
"geo": "US",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91552556",
"product": "Zombie",
"pkg": "com.ddup.zombiewaves.zw",
"geo": "US",
"channel": "Bigo",
"mode": "自投"
},
{
"campid": "91551606",
"product": "Zombie",
"pkg": "com.ddup.zombiewaves.zw",
"geo": "US",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91551412",
"product": "Zombie",
"pkg": "com.ddup.zombiewaves.zw",
"geo": "US",
"channel": "Newsbreak",
"mode": "其他"
},
{
"campid": "91551413",
"product": "Zombie",
"pkg": "com.ddup.zombiewaves.zw",
"geo": "US",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91551415",
"product": "Zombie",
"pkg": "com.ddup.zombiewaves.zw",
"geo": "US",
"channel": "传音",
"mode": "自投"
},
{
"campid": "91534918",
"product": "灵犀互动",
"pkg": "com.qookkagames.xczyw.gp.hk",
"geo": "HK",
"channel": "Bigo",
"mode": "自投"
},
{
"campid": "91533908",
"product": "灵犀互动",
"pkg": "com.qookkagames.xczyw.gp.hk",
"geo": "HK",
"channel": "Kwai",
"mode": "自投"
},
{
"campid": "91546640",
"product": "灵犀互动",
"pkg": "com.qookkagames.xczyw.gp.hk",
"geo": "HK",
"channel": "MediaGo",
"mode": "自投"
},
{
"campid": "91534913",
"product": "灵犀互动",
"pkg": "com.qookkagames.xczyw.gp.hk",
"geo": "HK",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539869",
"product": "灵犀互动",
"pkg": "com.qookkagames.xczyw.gp.hk",
"geo": "HK",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91550105",
"product": "灵犀互动",
"pkg": "com.qookkagames.xczyw.gp.hk",
"geo": "HK",
"channel": "Smartyads",
"mode": "自投"
},
{
"campid": "91544865",
"product": "灵犀互动",
"pkg": "com.qookkagames.xczyw.gp.hk",
"geo": "HK",
"channel": "传音",
"mode": "自投"
},
{
"campid": "91534919",
"product": "灵犀互动",
"pkg": "com.sialiagames.xczyw.gp.tw",
"geo": "TW",
"channel": "Bigo",
"mode": "自投"
},
{
"campid": "91533907",
"product": "灵犀互动",
"pkg": "com.sialiagames.xczyw.gp.tw",
"geo": "TW",
"channel": "Kwai",
"mode": "自投"
},
{
"campid": "91546639",
"product": "灵犀互动",
"pkg": "com.sialiagames.xczyw.gp.tw",
"geo": "TW",
"channel": "MediaGo",
"mode": "自投"
},
{
"campid": "91533906",
"product": "灵犀互动",
"pkg": "com.sialiagames.xczyw.gp.tw",
"geo": "TW",
"channel": "Mintegral",
"mode": "自投"
},
{
"campid": "91539870",
"product": "灵犀互动",
"pkg": "com.sialiagames.xczyw.gp.tw",
"geo": "TW",
"channel": "SPDragon(代投)",
"mode": "代投"
},
{
"campid": "91550106",
"product": "灵犀互动",
"pkg": "com.sialiagames.xczyw.gp.tw",
"geo": "TW",
"channel": "Smartyads",
"mode": "自投"
},
{
"campid": "91544866",
"product": "灵犀互动",
"pkg": "com.sialiagames.xczyw.gp.tw",
"geo": "TW",
"channel": "传音",
"mode": "自投"
}
]
+70 -2
View File
@@ -442,6 +442,36 @@
</table>
</div>
</div>
<!-- 模块3:Campaign ID 明细 -->
<div class="table-section" style="margin-top:24px;">
<h2>Campaign ID 明细(唯一识别值)</h2>
<div class="controls" style="box-shadow:none;padding:0 0 14px;background:transparent;">
<div class="control-group">
<label>产品</label>
<select id="campProductFilter"><option value="all">全部</option></select>
</div>
<div class="control-group">
<label>渠道</label>
<select id="campChannelFilter"><option value="all">全部</option></select>
</div>
</div>
<div class="table-wrapper">
<table id="campaignTable">
<thead>
<tr>
<th>Campaign ID</th>
<th>Product</th>
<th>Pkg / 包名</th>
<th>投放国家</th>
<th>渠道</th>
<th>投放方式</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div><!-- /tab-orders -->
<div class="updated-time" id="updatedTime"></div>
@@ -455,16 +485,19 @@
let rawData = [];
let orderData = [];
let campaignData = [];
let charts = {};
async function loadData() {
try {
const [dResp, oResp] = await Promise.all([
const [dResp, oResp, cResp] = await Promise.all([
fetch('data.json?' + Date.now()),
fetch('orders.json?' + Date.now())
fetch('orders.json?' + Date.now()),
fetch('campaigns.json?' + Date.now())
]);
rawData = await dResp.json();
orderData = await oResp.json();
campaignData = await cResp.json();
init();
} catch(e) {
console.error('Failed to load data:', e);
@@ -475,6 +508,7 @@
populateFilters();
renderAll();
renderOrders();
renderCampaigns();
setupTabs();
document.getElementById('pkgFilter').addEventListener('change', renderAll);
document.getElementById('dateFilter').addEventListener('change', renderAll);
@@ -595,6 +629,40 @@
`).join('');
}
function renderCampaigns() {
// 填充筛选器
const prods = [...new Set(campaignData.map(c => c.product))].sort();
const chans = [...new Set(campaignData.map(c => c.channel))].sort();
const pf = document.getElementById('campProductFilter');
const cf = document.getElementById('campChannelFilter');
prods.forEach(p => { const o=document.createElement('option'); o.value=p; o.textContent=p; pf.appendChild(o); });
chans.forEach(c => { const o=document.createElement('option'); o.value=c; o.textContent=c; cf.appendChild(o); });
pf.addEventListener('change', drawCampaignRows);
cf.addEventListener('change', drawCampaignRows);
drawCampaignRows();
}
function drawCampaignRows() {
const pf = document.getElementById('campProductFilter').value;
const cf = document.getElementById('campChannelFilter').value;
let data = campaignData;
if (pf !== 'all') data = data.filter(c => c.product === pf);
if (cf !== 'all') data = data.filter(c => c.channel === cf);
const modeColor = m => m === '自投' ? 'status-active' : (m === '代投' ? 'status-pause' : '');
const tbody = document.querySelector('#campaignTable tbody');
tbody.innerHTML = data.map(c => `
<tr>
<td class="num-cell" style="font-weight:600;color:#4338ca;">${c.campid}</td>
<td style="font-weight:500">${c.product}</td>
<td class="num-cell" style="font-size:11px">${c.pkg}</td>
<td><span class="pkg-tag">${c.geo}</span></td>
<td>${c.channel}</td>
<td><span class="status-badge ${modeColor(c.mode)}" style="padding:2px 8px;font-size:10px;">${c.mode}</span></td>
</tr>
`).join('');
}
function populateFilters() {
const pkgs = [...new Set(rawData.map(d => d.pkg))];
const sel = document.getElementById('pkgFilter');