feat: add persistent link summary
This commit is contained in:
+120
-17
@@ -3,12 +3,14 @@
|
||||
if (!templates?.length) return;
|
||||
|
||||
const { convertTrackingLink } = window.TrackingLinkConverter;
|
||||
|
||||
const HISTORY_KEY = 'traffic_link_history_v1';
|
||||
const MAX_HISTORY = 50;
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const platformSelect = $('platformSelect');
|
||||
const macroBody = $('macroTableBody');
|
||||
const output = $('urlOutput');
|
||||
let activeResult = null;
|
||||
let history = loadHistory();
|
||||
let inputTimer;
|
||||
|
||||
$('templateCount').textContent = `${templates.length} 个平台模板`;
|
||||
@@ -26,7 +28,9 @@
|
||||
|
||||
function renderMacros() {
|
||||
const query = $('macroSearch').value.trim().toLowerCase();
|
||||
const rows = activeTemplate().macros.filter(([macro, desc]) => `${macro} ${desc}`.toLowerCase().includes(query));
|
||||
const allMacros = activeTemplate().macros;
|
||||
const rows = allMacros.filter(([macro, desc]) => `${macro} ${desc}`.toLowerCase().includes(query));
|
||||
$('macroCount').textContent = `${allMacros.length} 个可用宏 · 点击展开`;
|
||||
macroBody.innerHTML = rows.length
|
||||
? rows.map(([macro, desc]) => `<tr><td><code>${escapeHtml(macro)}</code></td><td>${escapeHtml(desc)}</td></tr>`).join('')
|
||||
: '<tr><td colspan="2" class="table-empty">没有匹配的媒体宏</td></tr>';
|
||||
@@ -66,7 +70,7 @@
|
||||
if (!original) {
|
||||
showEmpty();
|
||||
if (showRequiredMessage) $('generatorMessage').textContent = '请先粘贴一条完整的 Tracking Link';
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -84,34 +88,112 @@
|
||||
$('conversionEmpty').classList.add('hidden');
|
||||
$('conversionResult').classList.remove('hidden');
|
||||
$('copyLink').disabled = false;
|
||||
return result;
|
||||
} catch (error) {
|
||||
showError(error.message === 'Invalid URL' ? '链接格式不正确,请粘贴包含 http:// 或 https:// 的完整链接' : error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function convertAndSave() {
|
||||
const result = convertFromInput(true);
|
||||
if (!result) return;
|
||||
saveHistory(result);
|
||||
$('generatorMessage').textContent = '转换完成,已加入平台链接总结';
|
||||
showToast('转换完成并已保存记录');
|
||||
}
|
||||
|
||||
function loadSample() {
|
||||
$('originalLink').value = 'https://trident.bigpinggo.com/l/click?advid=30&campid=91566789&platform=M_API_Self_Mintegral&pkg=com.salaryse.android&clickid={click_id}&offer_name={offer_name}&creative_id={creative_id}&creative_name={creative_name}&affiliate_id={affiliate_id}&idfa={idfa}&gaid={gaid}&imei={imei}&android_id={android_id}&mac={mac}&oaid={oaid}&uagent={ua}&os_platform={os_platform}&os_version={os_version}&device_model={device_model}&ip={ip}&country_code={country_code}&ext={ext}&rta_token={rta_token}&san_campid=91566789&adset_id=-1&c_cto=0';
|
||||
convertFromInput(false);
|
||||
}
|
||||
|
||||
function loadHistory() {
|
||||
try {
|
||||
const stored = JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]');
|
||||
return Array.isArray(stored) ? stored.filter((item) => item?.id && item?.url).slice(0, MAX_HISTORY) : [];
|
||||
} catch (_) { return []; }
|
||||
}
|
||||
|
||||
function persistHistory() {
|
||||
try {
|
||||
localStorage.setItem(HISTORY_KEY, JSON.stringify(history));
|
||||
return true;
|
||||
} catch (_) {
|
||||
showToast('浏览器无法保存记录,请检查存储权限');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function extractPkg(url) {
|
||||
try { return new URL(url).searchParams.get('pkg') || '—'; }
|
||||
catch (_) { return '—'; }
|
||||
}
|
||||
|
||||
function saveHistory(result) {
|
||||
const platform = activeTemplate().name;
|
||||
const pkg = extractPkg(result.url);
|
||||
history = history.filter((item) => item.url !== result.url);
|
||||
history.unshift({
|
||||
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||
platform,
|
||||
pkg,
|
||||
type: result.type,
|
||||
createdAt: new Date().toISOString(),
|
||||
url: result.url
|
||||
});
|
||||
history = history.slice(0, MAX_HISTORY);
|
||||
persistHistory();
|
||||
renderHistory();
|
||||
}
|
||||
|
||||
function formatTime(value) {
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return '—';
|
||||
return new Intl.DateTimeFormat('zh-CN', { year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', second:'2-digit', hour12:false }).format(date);
|
||||
}
|
||||
|
||||
function renderHistory() {
|
||||
$('historyCount').textContent = history.length;
|
||||
$('clearHistory').disabled = history.length === 0;
|
||||
$('historyEmpty').classList.toggle('hidden', history.length > 0);
|
||||
$('historyTableWrap').classList.toggle('hidden', history.length === 0);
|
||||
$('historyTableBody').innerHTML = history.map((item) => `<tr>
|
||||
<td><time datetime="${escapeHtml(item.createdAt)}">${escapeHtml(formatTime(item.createdAt))}</time></td>
|
||||
<td><span class="history-platform">${escapeHtml(item.platform)}</span></td>
|
||||
<td><code class="history-pkg">${escapeHtml(item.pkg)}</code></td>
|
||||
<td><div class="history-link"><span title="${escapeHtml(item.url)}">${escapeHtml(item.url)}</span><button type="button" data-action="copy" data-id="${escapeHtml(item.id)}">复制</button></div></td>
|
||||
<td><button class="delete-record" type="button" data-action="delete" data-id="${escapeHtml(item.id)}" aria-label="删除这条记录">删除</button></td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
|
||||
async function copyText(text) {
|
||||
try { await navigator.clipboard.writeText(text); }
|
||||
catch (_) {
|
||||
const area = document.createElement('textarea');
|
||||
area.value = text;
|
||||
area.style.position = 'fixed';
|
||||
area.style.opacity = '0';
|
||||
document.body.appendChild(area);
|
||||
area.select();
|
||||
document.execCommand('copy');
|
||||
area.remove();
|
||||
}
|
||||
}
|
||||
|
||||
async function copyUrl() {
|
||||
if (!activeResult) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(activeResult.url);
|
||||
} catch (_) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(output);
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
document.execCommand('copy');
|
||||
selection.removeAllRanges();
|
||||
}
|
||||
await copyText(activeResult.url);
|
||||
$('copyStatus').textContent = '已复制';
|
||||
showToast('转换后的平台链接已复制');
|
||||
}
|
||||
|
||||
function showToast(message) {
|
||||
const toast = $('toast');
|
||||
toast.textContent = '转换后的平台链接已复制';
|
||||
toast.textContent = message;
|
||||
toast.classList.add('show');
|
||||
setTimeout(() => toast.classList.remove('show'), 1800);
|
||||
clearTimeout(showToast.timer);
|
||||
showToast.timer = setTimeout(() => toast.classList.remove('show'), 1800);
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
@@ -120,10 +202,30 @@
|
||||
|
||||
platformSelect.addEventListener('change', renderPlatform);
|
||||
$('macroSearch').addEventListener('input', renderMacros);
|
||||
$('convertLink').addEventListener('click', () => convertFromInput(true));
|
||||
$('convertLink').addEventListener('click', convertAndSave);
|
||||
$('sampleLink').addEventListener('click', loadSample);
|
||||
$('copyLink').addEventListener('click', copyUrl);
|
||||
$('clearConverter').addEventListener('click', () => { $('originalLink').value = ''; showEmpty(); $('originalLink').focus(); });
|
||||
$('clearHistory').addEventListener('click', () => {
|
||||
if (!history.length || !window.confirm('确定清空全部平台链接记录吗?')) return;
|
||||
history = [];
|
||||
persistHistory();
|
||||
renderHistory();
|
||||
});
|
||||
$('historyTableBody').addEventListener('click', async (event) => {
|
||||
const button = event.target.closest('button[data-action]');
|
||||
if (!button) return;
|
||||
const item = history.find((record) => record.id === button.dataset.id);
|
||||
if (!item) return;
|
||||
if (button.dataset.action === 'copy') {
|
||||
await copyText(item.url);
|
||||
showToast('历史链接已复制');
|
||||
} else if (button.dataset.action === 'delete') {
|
||||
history = history.filter((record) => record.id !== item.id);
|
||||
persistHistory();
|
||||
renderHistory();
|
||||
}
|
||||
});
|
||||
$('originalLink').addEventListener('input', () => {
|
||||
clearTimeout(inputTimer);
|
||||
inputTimer = setTimeout(() => convertFromInput(false), 220);
|
||||
@@ -131,4 +233,5 @@
|
||||
|
||||
renderPlatform();
|
||||
showEmpty();
|
||||
renderHistory();
|
||||
})();
|
||||
|
||||
+3
-3
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user