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();
} catch (e) { console.error(e); }
}
async function loadWeeklyChart() {
try {
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 `
`;
}).join('');
} catch (e) { console.error(e); }
}
let currentDailyDate = '';
async function openDailyDetail(date) {
currentDailyDate = date;
document.getElementById('dailyDetailTitle').textContent = date + ' 收入';
document.getElementById('dailyNavDate').textContent = date;
document.getElementById('dailySummary').innerHTML = '加载中...
';
document.getElementById('dailyDetailList').innerHTML = '';
if (!document.getElementById('dailyDetailPage').classList.contains('active')) {
showPage('dailyDetailPage');
}
try {
const res = await fetch(API + '/api/stats/daily-detail?date=' + date, { headers: headers() });
if (!res.ok) return;
const data = await res.json();
document.getElementById('dailySummary').innerHTML = `
${data.total_income.toFixed(2)}
当日总收入
`;
const container = document.getElementById('dailyDetailList');
if (data.records.length === 0) {
container.innerHTML = '当日暂无收入记录
';
return;
}
container.innerHTML = data.records.map(r => {
const time = formatLocalTime(r.created_at);
const displayCategory = r.category === '红包收入' ? '红包' : (r.category || '-');
return `
${displayCategory}${r.boss ? ' · ' + r.boss : ''}
${(r.amount || 0).toFixed(2)}
${r.quantity ? r.quantity + '单 × ' + (r.category === '派单' ? (r.rebate || r.amount / r.quantity).toFixed(2) + '(单价' + (r.unit_price || 0).toFixed(2) + ')' : (r.unit_price || 0).toFixed(2)) : (r.source || '')}
${time}${r.partner ? ' · ' + r.partner : ''}
`;
}).join('');
} catch (e) { console.error(e); }
}
function switchDailyDate(offset) {
const d = new Date(currentDailyDate);
d.setDate(d.getDate() + offset);
openDailyDetail(formatLocalDate(d));
}
let monthlyDetailYear, monthlyDetailMonth;
function openMonthlyDetail(type) {
monthlyDetailYear = currentYear;
monthlyDetailMonth = currentMonth;
loadMonthlyDetail(type);
}
function switchMonthlyDetail(type, offset) {
monthlyDetailMonth += offset;
if (monthlyDetailMonth > 12) { monthlyDetailMonth = 1; monthlyDetailYear++; }
if (monthlyDetailMonth < 1) { monthlyDetailMonth = 12; monthlyDetailYear--; }
loadMonthlyDetail(type);
}
function openYearlyIncome() {
loadYearlyNetIncome();
}
async function loadYearlyNetIncome() {
document.getElementById('yearlyNetIncomeSummary').innerHTML = '加载中...
';
document.getElementById('yearlyNetIncomeList').innerHTML = '';
if (!document.getElementById('yearlyNetIncomePage').classList.contains('active')) {
showPage('yearlyNetIncomePage');
}
try {
const res = await fetch(API + '/api/stats/yearly-net-income', { headers: headers() });
if (!res.ok) return;
const data = await res.json();
const grandNetIncome = (data.grandTotal.total_income || 0) - (data.grandTotal.total_expense || 0);
const grandColor = grandNetIncome >= 0 ? '#27ae60' : '#e74c3c';
document.getElementById('yearlyNetIncomeSummary').innerHTML = `
${grandNetIncome.toFixed(2)}
累计净收入
`;
const container = document.getElementById('yearlyNetIncomeList');
const monthNames = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
if (data.yearlyStats.length === 0) {
container.innerHTML = '暂无数据
';
return;
}
container.innerHTML = data.yearlyStats.map(y => {
const year = y.year;
const income = y.total_income || 0;
const expense = y.total_expense || 0;
const net = income - expense;
const netColor = net >= 0 ? '#27ae60' : '#e74c3c';
const monthlyData = data.monthlyData[year] || [];
const monthlyHtml = monthlyData.map(m => {
const mIncome = m.total_income || 0;
const mExpense = m.total_expense || 0;
const mNet = mIncome - mExpense;
const mColor = mNet >= 0 ? '#27ae60' : '#e74c3c';
return `
${monthNames[parseInt(m.month) - 1]}
${mNet.toFixed(2)}
`;
}).join('');
return `
${year}年
${net.toFixed(2)}
收入: ${income.toFixed(2)} | 支出: ${expense.toFixed(2)}
${monthlyHtml}
`;
}).join('');
} catch (e) { console.error(e); }
}
async function loadMonthlyDetail(type) {
const pageId = type === 'income' ? 'monthlyIncomeDetailPage' : 'monthlyExpenseDetailPage';
const titleId = type === 'income' ? 'monthlyIncomeTitle' : 'monthlyExpenseTitle';
const navId = type === 'income' ? 'monthlyIncomeNav' : 'monthlyExpenseNav';
const summaryId = type === 'income' ? 'monthlyIncomeSummary' : 'monthlyExpenseSummary';
const listId = type === 'income' ? 'monthlyIncomeList' : 'monthlyExpenseList';
const label = type === 'income' ? '收入' : '支出';
document.getElementById(titleId).textContent = `${monthlyDetailYear}年${monthlyDetailMonth}月${label}`;
document.getElementById(navId).textContent = `${monthlyDetailYear}年${monthlyDetailMonth}月`;
document.getElementById(summaryId).innerHTML = '加载中...
';
document.getElementById(listId).innerHTML = '';
if (!document.getElementById(pageId).classList.contains('active')) {
showPage(pageId);
}
try {
const res = await fetch(API + '/api/stats/monthly-records?type=' + type + '&year=' + monthlyDetailYear + '&month=' + monthlyDetailMonth, { headers: headers() });
if (!res.ok) return;
const data = await res.json();
const color = type === 'income' ? '#27ae60' : '#e74c3c';
document.getElementById(summaryId).innerHTML = `
${data.total.toFixed(2)}
当月总${label}
`;
const container = document.getElementById(listId);
if (data.records.length === 0) {
container.innerHTML = '当月暂无' + label + '记录
';
return;
}
const grouped = {};
data.records.forEach(r => {
const date = formatLocalDate(r.created_at);
if (!grouped[date]) grouped[date] = [];
grouped[date].push(r);
});
let html = '';
for (const [date, records] of Object.entries(grouped)) {
html += `${date}
`;
records.forEach(r => {
const time = formatLocalTime(r.created_at);
const displayCategory = r.category === '红包收入' ? '红包' : (r.category || '-');
html += `
${displayCategory}${r.boss ? ' · ' + r.boss : r.source ? ' · ' + r.source : r.destination ? ' · ' + r.destination : ''}
${type === 'expense' ? '-' : ''}${(r.amount || 0).toFixed(2)}
${r.quantity ? r.quantity + '单 × ' + (r.category === '派单' ? (r.rebate || r.amount / r.quantity).toFixed(2) + '(单价' + (r.unit_price || 0).toFixed(2) + ')' : (r.unit_price || 0).toFixed(2)) : (r.note || '')}
${time}${r.partner ? ' · ' + r.partner : ''}
`;
});
html += '
';
}
container.innerHTML = html;
} catch (e) { console.error(e); }
}
async function loadBossRankingPreview() {
try {
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 = '暂无数据
';
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 `
${i + 1}
${r.boss}
${r.total_income.toFixed(2)}
`;
}).join('');
} catch (e) { console.error(e); }
}
let currentBossName = '';
let bossDetailFrom = 'homePage';
function openBossDetail(boss, from) {
currentBossName = boss;
bossDetailFrom = from || 'homePage';
loadBossDetail();
}
function bossDetailGoBack() {
showPage(bossDetailFrom);
}
async function loadBossDetail() {
document.getElementById('bossDetailTitle').textContent = currentBossName + ' 交易记录';
document.getElementById('bossDetailSummary').innerHTML = '加载中...
';
document.getElementById('bossDetailList').innerHTML = '';
showPage('bossDetailPage');
try {
const res = await fetch(API + '/api/stats/boss-records?boss=' + encodeURIComponent(currentBossName), { headers: headers() });
if (!res.ok) return;
const data = await res.json();
document.getElementById('bossDetailSummary').innerHTML = `
${data.boss_total_expense.toFixed(2)}
老板总支出
${data.my_income.toFixed(2)}
我的总收入
`;
const container = document.getElementById('bossDetailList');
if (data.records.length === 0) {
container.innerHTML = '暂无交易记录
';
return;
}
const grouped = {};
data.records.forEach(r => {
const d = new Date(r.created_at);
const key = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0');
if (!grouped[key]) grouped[key] = { records: [], total: 0 };
grouped[key].records.push(r);
grouped[key].total += r.amount || 0;
});
let html = '';
for (const [month, group] of Object.entries(grouped)) {
html += `${month} 收入 ${group.total.toFixed(2)}
`;
group.records.forEach(r => {
const time = formatLocalDate(r.created_at) + ' ' + formatLocalTime(r.created_at);
const displayCategory = r.category === '红包收入' ? '红包' : r.category;
html += `
${displayCategory}${r.partner ? ' · ' + r.partner : ''}
${(r.amount || 0).toFixed(2)}
${r.quantity ? r.quantity + '单 × ' + (r.category === '派单' ? (r.rebate || r.amount / r.quantity).toFixed(2) + '(单价' + (r.unit_price || 0).toFixed(2) + ')' : (r.unit_price || 0).toFixed(2)) : (r.note || '')}
${time}
`;
});
html += '
';
}
container.innerHTML = html;
} catch (e) { console.error(e); }
}
async function loadPartnerRankingPreview() {
try {
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 = '暂无数据
';
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 `
${i + 1}
${r.partner}
${r.dispatch_count}单
`;
}).join('');
} catch (e) { console.error(e); }
}
function openPartnerRankingPage() {
const now = new Date();
const y = now.getFullYear();
const m = String(now.getMonth() + 1).padStart(2, '0');
document.getElementById('partnerRankStartDate').value = y + '-' + m + '-01';
document.getElementById('partnerRankEndDate').value = y + '-' + m + '-' + String(now.getDate()).padStart(2, '0');
showPage('partnerRankingPage');
loadPartnerRankingDetail();
}
async function loadPartnerRankingDetail() {
const startDate = document.getElementById('partnerRankStartDate').value;
const endDate = document.getElementById('partnerRankEndDate').value;
let url = API + '/api/stats/partner-ranking';
const params = [];
if (startDate) params.push('startDate=' + startDate);
if (endDate) {
const d = new Date(endDate);
d.setDate(d.getDate() + 1);
params.push('endDate=' + formatLocalDate(d));
}
if (params.length) url += '?' + params.join('&');
console.log('partner-ranking url:', url);
try {
const res = await fetch(url, { headers: headers() });
console.log('partner-ranking res.ok:', res.ok);
if (!res.ok) return;
const data = await res.json();
console.log('partner-ranking data:', data);
const rows = data.rows || [];
console.log('rows:', rows);
const summary = data.summary || { total_partner_income: 0, total_rebate_income: 0, total_milkTea_income: 0 };
// 计算派单总数(从列表)
const totalDispatch = rows.reduce((sum, r) => sum + (r.dispatch_count || 0), 0);
console.log('totalDispatch:', totalDispatch);
// 更新统计卡片
document.getElementById('partnerTotalDispatchCount').textContent = totalDispatch;
document.getElementById('totalPartnerIncome').textContent = (summary.total_partner_income || 0).toFixed(2);
document.getElementById('totalRebateIncome').textContent = summary.total_rebate_income.toFixed(2);
document.getElementById('totalMilkTeaIncome').textContent = (summary.total_milkTea_income || 0).toFixed(2);
const container = document.getElementById('partnerRankingDetailList');
if (rows.length === 0) {
container.innerHTML = '暂无数据
';
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 `
${i + 1}
${r.partner}
${r.dispatch_count}单
`;
}).join('');
} catch (e) { console.error(e); }
}
let currentPartnerName = '';
let partnerDetailFrom = 'homePage';
function openPartnerDetail(partner, from) {
currentPartnerName = partner;
partnerDetailFrom = from || 'homePage';
loadPartnerDetail();
}
function partnerDetailGoBack() {
showPage(partnerDetailFrom);
}
async function loadPartnerDetail() {
document.getElementById('partnerDetailTitle').textContent = currentPartnerName + ' 交易记录';
document.getElementById('partnerDetailSummary').innerHTML = '加载中...
';
document.getElementById('partnerDetailList').innerHTML = '';
showPage('partnerDetailPage');
try {
const res = await fetch(API + '/api/stats/partner-records?partner=' + encodeURIComponent(currentPartnerName), { headers: headers() });
if (!res.ok) return;
const data = await res.json();
document.getElementById('partnerDetailSummary').innerHTML = `
${data.dispatch_count}
总派单数
${data.spread_income.toFixed(2)}
陪玩总收入
${data.rebate_income.toFixed(2)}
总返点收入
${data.other_income.toFixed(2)}
红包及其他
`;
const container = document.getElementById('partnerDetailList');
if (data.records.length === 0) {
container.innerHTML = '暂无交易记录
';
return;
}
const grouped = {};
data.records.forEach(r => {
const d = new Date(r.created_at);
const key = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0');
if (!grouped[key]) grouped[key] = { records: [], total: 0 };
grouped[key].records.push(r);
grouped[key].total += r.amount || 0;
});
let html = '';
for (const [month, group] of Object.entries(grouped)) {
html += `${month} 收入 ${group.total.toFixed(2)}
`;
group.records.forEach(r => {
const time = formatLocalDate(r.created_at) + ' ' + formatLocalTime(r.created_at);
const displayCategory = r.category === '红包收入' ? '红包' : r.category;
html += `
${displayCategory}${r.boss ? ' · ' + r.boss : ''}
${(r.amount || 0).toFixed(2)}
${r.quantity ? r.quantity + '单 × ' + (r.category === '派单' ? (r.rebate || r.amount / r.quantity).toFixed(2) + '(单价' + (r.unit_price || 0).toFixed(2) + ')' : (r.unit_price || 0).toFixed(2)) : (r.source || '')}
${time}
`;
});
html += '
';
}
container.innerHTML = html;
} catch (e) { console.error(e); }
}