perf: 并行加载首页统计数据
将原来顺序执行的4个API调用改为并行执行(Promise.all), 显著减少首页加载时间。同时提取了渲染函数 renderWeeklyChart、 renderBossRankingPreview、renderPartnerRankingPreview,提高代码复用性。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
4656f3f9ca
commit
a03e92d437
+95
-59
@@ -1,22 +1,42 @@
|
||||
async function loadStats() {
|
||||
try {
|
||||
const res = await fetch(API + '/api/stats/monthly?year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
const s = await res.json();
|
||||
document.getElementById('takeCount').textContent = s.take_order_count;
|
||||
document.getElementById('dispatchCount').textContent = s.dispatch_order_count;
|
||||
document.getElementById('takeIncome').textContent = '' + s.take_order_income.toFixed(2);
|
||||
document.getElementById('dispatchIncome').textContent = '' + s.dispatch_order_income.toFixed(2);
|
||||
document.getElementById('redEnvelopeCount').textContent = s.redEnvelope_count || 0;
|
||||
document.getElementById('redEnvelopeIncome').textContent = '' + (s.redEnvelope_income || 0).toFixed(2);
|
||||
document.getElementById('milkTeaCount').textContent = s.milkTea_count || 0;
|
||||
document.getElementById('milkTeaIncome').textContent = '' + (s.milkTea_income || 0).toFixed(2);
|
||||
document.getElementById('totalIncome').textContent = '' + s.total_income.toFixed(2);
|
||||
document.getElementById('totalExpense').textContent = '' + s.total_expense.toFixed(2);
|
||||
document.getElementById('netIncome').textContent = '' + (s.total_income - s.total_expense).toFixed(2);
|
||||
loadBossRankingPreview();
|
||||
loadPartnerRankingPreview();
|
||||
loadWeeklyChart();
|
||||
// 并行加载所有数据
|
||||
const [monthlyRes, bossRankRes, partnerRankRes, weeklyRes] = await Promise.all([
|
||||
fetch(API + '/api/stats/monthly?year=' + currentYear + '&month=' + currentMonth, { headers: headers() }),
|
||||
fetch(API + '/api/stats/boss-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() }),
|
||||
fetch(API + '/api/stats/partner-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() }),
|
||||
fetch(API + '/api/stats/daily-income', { headers: headers() })
|
||||
]);
|
||||
|
||||
if (monthlyRes.ok) {
|
||||
const s = await monthlyRes.json();
|
||||
document.getElementById('takeCount').textContent = s.take_order_count;
|
||||
document.getElementById('dispatchCount').textContent = s.dispatch_order_count;
|
||||
document.getElementById('takeIncome').textContent = '' + s.take_order_income.toFixed(2);
|
||||
document.getElementById('dispatchIncome').textContent = '' + s.dispatch_order_income.toFixed(2);
|
||||
document.getElementById('redEnvelopeCount').textContent = s.redEnvelope_count || 0;
|
||||
document.getElementById('redEnvelopeIncome').textContent = '' + (s.redEnvelope_income || 0).toFixed(2);
|
||||
document.getElementById('milkTeaCount').textContent = s.milkTea_count || 0;
|
||||
document.getElementById('milkTeaIncome').textContent = '' + (s.milkTea_income || 0).toFixed(2);
|
||||
document.getElementById('totalIncome').textContent = '' + s.total_income.toFixed(2);
|
||||
document.getElementById('totalExpense').textContent = '' + s.total_expense.toFixed(2);
|
||||
document.getElementById('netIncome').textContent = '' + (s.total_income - s.total_expense).toFixed(2);
|
||||
}
|
||||
|
||||
if (bossRankRes.ok) {
|
||||
const data = await bossRankRes.json();
|
||||
renderBossRankingPreview(data.rows || []);
|
||||
}
|
||||
|
||||
if (partnerRankRes.ok) {
|
||||
const data = await partnerRankRes.json();
|
||||
renderPartnerRankingPreview(data.rows || []);
|
||||
}
|
||||
|
||||
if (weeklyRes.ok) {
|
||||
const days = await weeklyRes.json();
|
||||
renderWeeklyChart(days);
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
@@ -25,21 +45,29 @@ async function loadWeeklyChart() {
|
||||
const res = await fetch(API + '/api/stats/daily-income', { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
const days = await res.json();
|
||||
const container = document.getElementById('weeklyChartBars');
|
||||
const max = Math.max(...days.map(d => d.income), 1);
|
||||
container.innerHTML = days.map(d => {
|
||||
const pct = Math.max((d.income / max) * 100, 3);
|
||||
const label = d.date.slice(5).replace('-', '/');
|
||||
const amount = d.income > 0 ? '' + d.income.toFixed(0) : '0';
|
||||
return `<div class="weekly-bar-col" onclick="openDailyDetail('${d.date}')">
|
||||
<span class="weekly-bar-amount">${amount}</span>
|
||||
<div class="weekly-bar" style="height:${pct}%"></div>
|
||||
<span class="weekly-bar-date">${label}</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
renderWeeklyChart(days);
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
function renderWeeklyChart(days) {
|
||||
const container = document.getElementById('weeklyChartBars');
|
||||
if (!days || days.length === 0) {
|
||||
container.innerHTML = '<div class="ranking-empty">暂无数据</div>';
|
||||
return;
|
||||
}
|
||||
const max = Math.max(...days.map(d => d.income), 1);
|
||||
container.innerHTML = days.map(d => {
|
||||
const pct = Math.max((d.income / max) * 100, 3);
|
||||
const label = d.date.slice(5).replace('-', '/');
|
||||
const amount = d.income > 0 ? '' + d.income.toFixed(0) : '0';
|
||||
return `<div class="weekly-bar-col" onclick="openDailyDetail('${d.date}')">
|
||||
<span class="weekly-bar-amount">${amount}</span>
|
||||
<div class="weekly-bar" style="height:${pct}%"></div>
|
||||
<span class="weekly-bar-date">${label}</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
let currentDailyDate = '';
|
||||
|
||||
async function openDailyDetail(date) {
|
||||
@@ -232,24 +260,28 @@ async function loadBossRankingPreview() {
|
||||
const res = await fetch(API + '/api/stats/boss-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
const rows = await res.json();
|
||||
const container = document.getElementById('rankingPreviewList');
|
||||
if (rows.length === 0) {
|
||||
container.innerHTML = '<div class="ranking-empty">暂无数据</div>';
|
||||
return;
|
||||
}
|
||||
const max = Math.max(...rows.map(r => r.total_income), 1);
|
||||
container.innerHTML = rows.map((r, i) => {
|
||||
const pct = Math.max((r.total_income / max) * 100, 2);
|
||||
return `<div class="hbar-row" onclick="openBossDetail('${r.boss.replace(/'/g, "\\'")}', 'homePage')">
|
||||
<div class="hbar-index ${i < 3 ? 'top' : ''}">${i + 1}</div>
|
||||
<div class="hbar-label" title="${r.boss}">${r.boss}</div>
|
||||
<div class="hbar-track"><div class="hbar-fill" style="width:${pct}%"></div></div>
|
||||
<div class="hbar-value">${r.total_income.toFixed(2)}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
renderBossRankingPreview(rows);
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
function renderBossRankingPreview(rows) {
|
||||
const container = document.getElementById('rankingPreviewList');
|
||||
if (!rows || rows.length === 0) {
|
||||
container.innerHTML = '<div class="ranking-empty">暂无数据</div>';
|
||||
return;
|
||||
}
|
||||
const max = Math.max(...rows.map(r => r.total_income), 1);
|
||||
container.innerHTML = rows.map((r, i) => {
|
||||
const pct = Math.max((r.total_income / max) * 100, 2);
|
||||
return `<div class="hbar-row" onclick="openBossDetail('${r.boss.replace(/'/g, "\\'")}', 'homePage')">
|
||||
<div class="hbar-index ${i < 3 ? 'top' : ''}">${i + 1}</div>
|
||||
<div class="hbar-label" title="${r.boss}">${r.boss}</div>
|
||||
<div class="hbar-track"><div class="hbar-fill" style="width:${pct}%"></div></div>
|
||||
<div class="hbar-value">${r.total_income.toFixed(2)}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
let currentBossName = '';
|
||||
let bossDetailFrom = 'homePage';
|
||||
|
||||
@@ -326,24 +358,28 @@ async function loadPartnerRankingPreview() {
|
||||
const res = await fetch(API + '/api/stats/partner-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
const rows = await res.json();
|
||||
const container = document.getElementById('partnerRankingPreviewList');
|
||||
if (rows.length === 0) {
|
||||
container.innerHTML = '<div class="ranking-empty">暂无数据</div>';
|
||||
return;
|
||||
}
|
||||
const max = Math.max(...rows.map(r => r.dispatch_count), 1);
|
||||
container.innerHTML = rows.map((r, i) => {
|
||||
const pct = Math.max((r.dispatch_count / max) * 100, 2);
|
||||
return `<div class="hbar-row" onclick="openPartnerDetail('${r.partner.replace(/'/g, "\\'")}', 'homePage')">
|
||||
<div class="hbar-index ${i < 3 ? 'top' : ''}">${i + 1}</div>
|
||||
<div class="hbar-label" title="${r.partner}">${r.partner}</div>
|
||||
<div class="hbar-track"><div class="hbar-fill" style="width:${pct}%"></div></div>
|
||||
<div class="hbar-value">${r.dispatch_count}单</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
renderPartnerRankingPreview(rows);
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
function renderPartnerRankingPreview(rows) {
|
||||
const container = document.getElementById('partnerRankingPreviewList');
|
||||
if (!rows || rows.length === 0) {
|
||||
container.innerHTML = '<div class="ranking-empty">暂无数据</div>';
|
||||
return;
|
||||
}
|
||||
const max = Math.max(...rows.map(r => r.dispatch_count), 1);
|
||||
container.innerHTML = rows.map((r, i) => {
|
||||
const pct = Math.max((r.dispatch_count / max) * 100, 2);
|
||||
return `<div class="hbar-row" onclick="openPartnerDetail('${r.partner.replace(/'/g, "\\'")}', 'homePage')">
|
||||
<div class="hbar-index ${i < 3 ? 'top' : ''}">${i + 1}</div>
|
||||
<div class="hbar-label" title="${r.partner}">${r.partner}</div>
|
||||
<div class="hbar-track"><div class="hbar-fill" style="width:${pct}%"></div></div>
|
||||
<div class="hbar-value">${r.dispatch_count}单</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function openPartnerRankingPage() {
|
||||
const now = new Date();
|
||||
const y = now.getFullYear();
|
||||
|
||||
Reference in New Issue
Block a user