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() {
|
async function loadStats() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(API + '/api/stats/monthly?year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
|
// 并行加载所有数据
|
||||||
if (!res.ok) return;
|
const [monthlyRes, bossRankRes, partnerRankRes, weeklyRes] = await Promise.all([
|
||||||
const s = await res.json();
|
fetch(API + '/api/stats/monthly?year=' + currentYear + '&month=' + currentMonth, { headers: headers() }),
|
||||||
document.getElementById('takeCount').textContent = s.take_order_count;
|
fetch(API + '/api/stats/boss-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() }),
|
||||||
document.getElementById('dispatchCount').textContent = s.dispatch_order_count;
|
fetch(API + '/api/stats/partner-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() }),
|
||||||
document.getElementById('takeIncome').textContent = '' + s.take_order_income.toFixed(2);
|
fetch(API + '/api/stats/daily-income', { headers: headers() })
|
||||||
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);
|
if (monthlyRes.ok) {
|
||||||
document.getElementById('milkTeaCount').textContent = s.milkTea_count || 0;
|
const s = await monthlyRes.json();
|
||||||
document.getElementById('milkTeaIncome').textContent = '' + (s.milkTea_income || 0).toFixed(2);
|
document.getElementById('takeCount').textContent = s.take_order_count;
|
||||||
document.getElementById('totalIncome').textContent = '' + s.total_income.toFixed(2);
|
document.getElementById('dispatchCount').textContent = s.dispatch_order_count;
|
||||||
document.getElementById('totalExpense').textContent = '' + s.total_expense.toFixed(2);
|
document.getElementById('takeIncome').textContent = '' + s.take_order_income.toFixed(2);
|
||||||
document.getElementById('netIncome').textContent = '' + (s.total_income - s.total_expense).toFixed(2);
|
document.getElementById('dispatchIncome').textContent = '' + s.dispatch_order_income.toFixed(2);
|
||||||
loadBossRankingPreview();
|
document.getElementById('redEnvelopeCount').textContent = s.redEnvelope_count || 0;
|
||||||
loadPartnerRankingPreview();
|
document.getElementById('redEnvelopeIncome').textContent = '' + (s.redEnvelope_income || 0).toFixed(2);
|
||||||
loadWeeklyChart();
|
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); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,21 +45,29 @@ async function loadWeeklyChart() {
|
|||||||
const res = await fetch(API + '/api/stats/daily-income', { headers: headers() });
|
const res = await fetch(API + '/api/stats/daily-income', { headers: headers() });
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
const days = await res.json();
|
const days = await res.json();
|
||||||
const container = document.getElementById('weeklyChartBars');
|
renderWeeklyChart(days);
|
||||||
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('');
|
|
||||||
} catch (e) { console.error(e); }
|
} 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 = '';
|
let currentDailyDate = '';
|
||||||
|
|
||||||
async function openDailyDetail(date) {
|
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() });
|
const res = await fetch(API + '/api/stats/boss-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
const rows = await res.json();
|
const rows = await res.json();
|
||||||
const container = document.getElementById('rankingPreviewList');
|
renderBossRankingPreview(rows);
|
||||||
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('');
|
|
||||||
} catch (e) { console.error(e); }
|
} 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 currentBossName = '';
|
||||||
let bossDetailFrom = 'homePage';
|
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() });
|
const res = await fetch(API + '/api/stats/partner-ranking/preview?year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
const rows = await res.json();
|
const rows = await res.json();
|
||||||
const container = document.getElementById('partnerRankingPreviewList');
|
renderPartnerRankingPreview(rows);
|
||||||
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('');
|
|
||||||
} catch (e) { console.error(e); }
|
} 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() {
|
function openPartnerRankingPage() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const y = now.getFullYear();
|
const y = now.getFullYear();
|
||||||
|
|||||||
Reference in New Issue
Block a user