feat: link MTG data to coverage filters

This commit is contained in:
王源
2026-10-14 17:33:45 +08:00
parent bb10731ddc
commit ff2875b0ad
+48 -2
View File
@@ -513,6 +513,13 @@
search.addEventListener('input', () => { mtgState.page = 1; applyMtgFilters(); }); search.addEventListener('input', () => { mtgState.page = 1; applyMtgFilters(); });
country.addEventListener('change', () => { mtgState.page = 1; applyMtgFilters(); }); country.addEventListener('change', () => { mtgState.page = 1; applyMtgFilters(); });
match.addEventListener('change', () => { mtgState.page = 1; applyMtgFilters(); }); match.addEventListener('change', () => { mtgState.page = 1; applyMtgFilters(); });
['search', 'countryFilter', 'platformFilter', 'statusFilter'].forEach(id => {
const control = document.getElementById(id);
control.addEventListener(id === 'search' ? 'input' : 'change', () => {
mtgState.page = 1;
applyMtgFilters();
});
});
exportButton.addEventListener('click', exportMtgCsv); exportButton.addEventListener('click', exportMtgCsv);
applyMtgFilters(); applyMtgFilters();
} }
@@ -521,15 +528,37 @@
const query = document.getElementById('mtgSearch').value.trim().toLocaleLowerCase(); const query = document.getElementById('mtgSearch').value.trim().toLocaleLowerCase();
const country = document.getElementById('mtgCountry').value; const country = document.getElementById('mtgCountry').value;
const match = document.getElementById('mtgMatch').value; const match = document.getElementById('mtgMatch').value;
const mainSearch = document.getElementById('search').value.trim().toLocaleLowerCase();
const mainCountry = document.getElementById('countryFilter').value;
const mainPlatform = document.getElementById('platformFilter').value;
const mainStatusControl = document.getElementById('statusFilter');
const mainStatus = mainStatusControl.value;
const coverageByKey = new Map(
state.data.records.map(row => [`${row.pkg.toLocaleLowerCase()}|${row.country}`, row])
);
mtgState.filtered = state.data.mtg1013.records.filter(row => { mtgState.filtered = state.data.mtg1013.records.filter(row => {
const status = mtgMatchStatus(row).key; const status = mtgMatchStatus(row).key;
const coverage = coverageByKey.get(`${row.pkg.toLocaleLowerCase()}|${row.country}`);
const coverageStatus = coverage ? statusOf(coverage).key : 'unmatched';
const matched = match === 'all' const matched = match === 'all'
|| status === match || status === match
|| (match === 'matched' && row.order_match); || (match === 'matched' && row.order_match);
const mainSearchable = `${row.adpingid} ${row.pkg} ${coverage ? coverage.product : ''} ${coverage ? coverage.adpingids.join(' ') : ''}`.toLocaleLowerCase();
return (!query || `${row.adpingid} ${row.pkg}`.toLocaleLowerCase().includes(query)) return (!query || `${row.adpingid} ${row.pkg}`.toLocaleLowerCase().includes(query))
&& (country === 'all' || row.country === country) && (country === 'all' || row.country === country)
&& matched; && matched
&& (!mainSearch || mainSearchable.includes(mainSearch))
&& (mainCountry === 'all' || row.country === mainCountry)
&& (mainPlatform === 'all' || Boolean(coverage && coverage.platforms.includes(mainPlatform)))
&& (mainStatus === 'all' || coverageStatus === mainStatus);
}); });
const linked = [mainStatusControl.options[mainStatusControl.selectedIndex].text];
if (mainCountry !== 'all') linked.push(`国家 ${mainCountry}`);
if (mainPlatform !== 'all') linked.push(`平台 ${mainPlatform}`);
if (mainSearch) linked.push(`搜索 ${mainSearch}`);
mtgState.linkedLabel = linked.join(' · ');
renderMtgResults(); renderMtgResults();
} }
@@ -539,10 +568,27 @@
mtgState.page = Math.min(mtgState.page, totalPages); mtgState.page = Math.min(mtgState.page, totalPages);
const start = (mtgState.page - 1) * PAGE_SIZE; const start = (mtgState.page - 1) * PAGE_SIZE;
const rows = mtgState.filtered.slice(start, start + PAGE_SIZE); const rows = mtgState.filtered.slice(start, start + PAGE_SIZE);
const clicks = mtgState.filtered.reduce((sum, row) => sum + row.valid_clicks, 0);
const installs = mtgState.filtered.reduce((sum, row) => sum + row.installs, 0);
const revenue = mtgState.filtered.reduce((sum, row) => sum + row.revenue, 0);
const cost = mtgState.filtered.reduce((sum, row) => sum + row.cost, 0);
const matchedRows = mtgState.filtered.filter(row => row.order_match).length;
const unmatchedRows = mtgState.filtered.length - matchedRows;
const cards = panel.querySelectorAll('.kpi');
const setCard = (index, value, sub) => {
cards[index].querySelector('.value').textContent = value;
cards[index].querySelector('.sub').textContent = sub;
};
setCard(0, nf.format(clicks), `${nf.format(mtgState.filtered.length)} 条筛选明细`);
setCard(1, nf.format(installs), `安装率 ${clicks ? percent(installs / clicks) : '-'}`);
setCard(2, money(revenue), `利润 ${money(revenue - cost)}`);
setCard(3, money(cost), `${nf.format(matchedRows)} 条已关联订单`);
setCard(4, cost ? percent(revenue / cost) : '-', `${nf.format(unmatchedRows)} 条未关联订单`);
const meta = panel.querySelector('.result-meta'); const meta = panel.querySelector('.result-meta');
meta.textContent = ''; meta.textContent = '';
meta.append( meta.append(
el('span', '', `筛选结果 ${nf.format(mtgState.filtered.length)} 条`), el('span', '', `筛选结果 ${nf.format(mtgState.filtered.length)} 条 · 联动:${mtgState.linkedLabel}`),
el('span', '', `第 ${mtgState.page} / ${totalPages} 页`) el('span', '', `第 ${mtgState.page} / ${totalPages} 页`)
); );