fix: 修复柱状图高度比例计算并添加月度筛选功能
- 修复最近7天收入柱状图高度使用百分比导致比例失真问题,改为像素计算 - 老板/陪玩交易详情页添加"全部/当月"切换按钮 - 老板/陪玩交易详情页添加月度导航切换功能 - 老板/陪玩交易详情页添加自动加载更多数据功能 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
3d8d03a66e
commit
14927f74aa
+307
-52
@@ -56,13 +56,14 @@ function renderWeeklyChart(days) {
|
||||
return;
|
||||
}
|
||||
const max = Math.max(...days.map(d => d.income), 1);
|
||||
const barMaxHeight = 110; // 柱状图最大可用像素高度
|
||||
container.innerHTML = days.map(d => {
|
||||
const pct = Math.max((d.income / max) * 100, 3);
|
||||
const height = Math.max((d.income / max) * barMaxHeight, 4);
|
||||
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>
|
||||
<div class="weekly-bar" style="height:${height}px"></div>
|
||||
<span class="weekly-bar-date">${label}</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
@@ -284,46 +285,145 @@ function renderBossRankingPreview(rows) {
|
||||
|
||||
let currentBossName = '';
|
||||
let bossDetailFrom = 'homePage';
|
||||
let bossDetailYear = 0;
|
||||
let bossDetailMonth = 0;
|
||||
let bossDetailScopeAll = false;
|
||||
let bossDetailPage = 1;
|
||||
let bossDetailLimit = 20;
|
||||
let bossDetailTotalCount = 0;
|
||||
let bossDetailAllRecords = [];
|
||||
let bossDetailLoading = false;
|
||||
let bossDetailScrollHandler = null;
|
||||
|
||||
function openBossDetail(boss, from) {
|
||||
currentBossName = boss;
|
||||
bossDetailFrom = from || 'homePage';
|
||||
bossDetailYear = currentYear;
|
||||
bossDetailMonth = currentMonth;
|
||||
// 从排行榜进入时默认显示全部
|
||||
bossDetailScopeAll = (from === 'rankingPage' || from === 'takeRankingPage');
|
||||
bossDetailPage = 1;
|
||||
bossDetailLimit = bossDetailScopeAll ? 500 : 20;
|
||||
bossDetailTotalCount = 0;
|
||||
bossDetailAllRecords = [];
|
||||
updateBossDetailNav();
|
||||
loadBossDetail();
|
||||
}
|
||||
|
||||
function bossDetailGoBack() {
|
||||
if (bossDetailScrollHandler) {
|
||||
window.removeEventListener('scroll', bossDetailScrollHandler);
|
||||
bossDetailScrollHandler = null;
|
||||
}
|
||||
showPage(bossDetailFrom);
|
||||
}
|
||||
|
||||
async function loadBossDetail() {
|
||||
document.getElementById('bossDetailTitle').textContent = currentBossName + ' 交易记录';
|
||||
document.getElementById('bossDetailSummary').innerHTML = '<div class="ranking-empty">加载中...</div>';
|
||||
document.getElementById('bossDetailList').innerHTML = '';
|
||||
showPage('bossDetailPage');
|
||||
function updateBossDetailNav() {
|
||||
const scopeEl = document.getElementById('bossDetailScope');
|
||||
const allBtn = document.getElementById('bossDetailAllBtn');
|
||||
if (bossDetailScopeAll) {
|
||||
scopeEl.textContent = '全部';
|
||||
allBtn.textContent = '当月';
|
||||
allBtn.classList.add('active');
|
||||
} else {
|
||||
scopeEl.textContent = `${bossDetailYear}年${bossDetailMonth}月`;
|
||||
allBtn.textContent = '全部';
|
||||
allBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
function switchBossDetailScope(offset) {
|
||||
if (bossDetailScopeAll) return;
|
||||
bossDetailMonth += offset;
|
||||
if (bossDetailMonth > 12) { bossDetailMonth = 1; bossDetailYear++; }
|
||||
if (bossDetailMonth < 1) { bossDetailMonth = 12; bossDetailYear--; }
|
||||
bossDetailPage = 1;
|
||||
bossDetailAllRecords = [];
|
||||
updateBossDetailNav();
|
||||
loadBossDetail();
|
||||
}
|
||||
|
||||
function toggleBossDetailAll() {
|
||||
bossDetailScopeAll = !bossDetailScopeAll;
|
||||
bossDetailPage = 1;
|
||||
bossDetailAllRecords = [];
|
||||
if (bossDetailScopeAll) {
|
||||
bossDetailLimit = 500;
|
||||
} else {
|
||||
bossDetailLimit = 20;
|
||||
bossDetailYear = currentYear;
|
||||
bossDetailMonth = currentMonth;
|
||||
}
|
||||
updateBossDetailNav();
|
||||
loadBossDetail();
|
||||
}
|
||||
|
||||
function loadMoreBossDetail() {
|
||||
bossDetailPage++;
|
||||
loadBossDetail(true);
|
||||
}
|
||||
|
||||
async function loadBossDetail(append) {
|
||||
if (bossDetailLoading) return;
|
||||
bossDetailLoading = true;
|
||||
|
||||
if (!append) {
|
||||
document.getElementById('bossDetailTitle').textContent = currentBossName + ' 交易记录';
|
||||
document.getElementById('bossDetailSummary').innerHTML = '<div class="ranking-empty">加载中...</div>';
|
||||
document.getElementById('bossDetailList').innerHTML = '';
|
||||
showPage('bossDetailPage');
|
||||
}
|
||||
|
||||
let url = API + '/api/stats/boss-records?boss=' + encodeURIComponent(currentBossName) + '&page=' + bossDetailPage + '&limit=' + bossDetailLimit;
|
||||
if (!bossDetailScopeAll) {
|
||||
url += '&year=' + bossDetailYear + '&month=' + bossDetailMonth;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(API + '/api/stats/boss-records?boss=' + encodeURIComponent(currentBossName), { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
const res = await fetch(url, { headers: headers() });
|
||||
if (!res.ok) { bossDetailLoading = false; return; }
|
||||
const data = await res.json();
|
||||
document.getElementById('bossDetailSummary').innerHTML = `
|
||||
<div style="display:flex;justify-content:space-around;flex-wrap:wrap;gap:8px">
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#e74c3c">${data.boss_total_expense.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">老板总支出</div>
|
||||
|
||||
if (!append) {
|
||||
document.getElementById('bossDetailSummary').innerHTML = `
|
||||
<div style="display:flex;justify-content:space-around;flex-wrap:wrap;gap:8px">
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#e74c3c">${data.boss_total_expense.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">老板总支出</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#27ae60">${data.my_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">我的总收入</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#3498db">${data.take_count || 0}</div>
|
||||
<div class="daily-summary-label">接单数</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#9b59b6">${data.dispatch_count || 0}</div>
|
||||
<div class="daily-summary-label">派单数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#27ae60">${data.my_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">我的总收入</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
bossDetailAllRecords = data.records || [];
|
||||
setupBossDetailScroll();
|
||||
} else {
|
||||
bossDetailAllRecords = bossDetailAllRecords.concat(data.records || []);
|
||||
}
|
||||
bossDetailTotalCount = data.totalCount || 0;
|
||||
|
||||
const container = document.getElementById('bossDetailList');
|
||||
if (data.records.length === 0) {
|
||||
const moreBtn = document.getElementById('bossDetailMore');
|
||||
|
||||
if (bossDetailAllRecords.length === 0) {
|
||||
container.innerHTML = '<div class="ranking-empty">暂无交易记录</div>';
|
||||
if (moreBtn) moreBtn.style.display = 'none';
|
||||
bossDetailLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const grouped = {};
|
||||
data.records.forEach(r => {
|
||||
bossDetailAllRecords.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 };
|
||||
@@ -350,7 +450,42 @@ async function loadBossDetail() {
|
||||
html += '</div>';
|
||||
}
|
||||
container.innerHTML = html;
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
// Show/hide load more indicator
|
||||
if (moreBtn) {
|
||||
if (bossDetailAllRecords.length < bossDetailTotalCount) {
|
||||
moreBtn.style.display = 'block';
|
||||
moreBtn.textContent = '加载中... (' + bossDetailAllRecords.length + '/' + bossDetailTotalCount + ')';
|
||||
} else {
|
||||
moreBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
bossDetailLoading = false;
|
||||
} catch (e) {
|
||||
bossDetailLoading = false;
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function setupBossDetailScroll() {
|
||||
if (bossDetailScrollHandler) {
|
||||
window.removeEventListener('scroll', bossDetailScrollHandler);
|
||||
}
|
||||
bossDetailScrollHandler = function() {
|
||||
if (bossDetailLoading) return;
|
||||
if (bossDetailScopeAll && bossDetailAllRecords.length >= bossDetailTotalCount) {
|
||||
window.removeEventListener('scroll', bossDetailScrollHandler);
|
||||
return;
|
||||
}
|
||||
const scrollY = window.scrollY;
|
||||
const windowHeight = window.innerHeight;
|
||||
const docHeight = document.documentElement.scrollHeight;
|
||||
if (scrollY + windowHeight >= docHeight - 200) {
|
||||
bossDetailPage++;
|
||||
loadBossDetail(true);
|
||||
}
|
||||
};
|
||||
window.addEventListener('scroll', bossDetailScrollHandler);
|
||||
}
|
||||
|
||||
async function loadPartnerRankingPreview() {
|
||||
@@ -443,54 +578,140 @@ async function loadPartnerRankingDetail() {
|
||||
|
||||
let currentPartnerName = '';
|
||||
let partnerDetailFrom = 'homePage';
|
||||
let partnerDetailYear = 0;
|
||||
let partnerDetailMonth = 0;
|
||||
let partnerDetailScopeAll = false;
|
||||
let partnerDetailPage = 1;
|
||||
let partnerDetailLimit = 20;
|
||||
let partnerDetailTotalCount = 0;
|
||||
let partnerDetailAllRecords = [];
|
||||
let partnerDetailLoading = false;
|
||||
let partnerDetailScrollHandler = null;
|
||||
|
||||
function openPartnerDetail(partner, from) {
|
||||
currentPartnerName = partner;
|
||||
partnerDetailFrom = from || 'homePage';
|
||||
partnerDetailYear = currentYear;
|
||||
partnerDetailMonth = currentMonth;
|
||||
// 从排行榜进入时默认显示全部
|
||||
partnerDetailScopeAll = (from === 'dispatchRankingPage' || from === 'partnerRankingPage');
|
||||
partnerDetailPage = 1;
|
||||
partnerDetailLimit = partnerDetailScopeAll ? 500 : 20;
|
||||
partnerDetailTotalCount = 0;
|
||||
partnerDetailAllRecords = [];
|
||||
updatePartnerDetailNav();
|
||||
loadPartnerDetail();
|
||||
}
|
||||
|
||||
function partnerDetailGoBack() {
|
||||
if (partnerDetailScrollHandler) {
|
||||
window.removeEventListener('scroll', partnerDetailScrollHandler);
|
||||
partnerDetailScrollHandler = null;
|
||||
}
|
||||
showPage(partnerDetailFrom);
|
||||
}
|
||||
|
||||
async function loadPartnerDetail() {
|
||||
document.getElementById('partnerDetailTitle').textContent = currentPartnerName + ' 交易记录';
|
||||
document.getElementById('partnerDetailSummary').innerHTML = '<div class="ranking-empty">加载中...</div>';
|
||||
document.getElementById('partnerDetailList').innerHTML = '';
|
||||
showPage('partnerDetailPage');
|
||||
function updatePartnerDetailNav() {
|
||||
const scopeEl = document.getElementById('partnerDetailScope');
|
||||
const allBtn = document.getElementById('partnerDetailAllBtn');
|
||||
if (partnerDetailScopeAll) {
|
||||
scopeEl.textContent = '全部';
|
||||
allBtn.textContent = '当月';
|
||||
allBtn.classList.add('active');
|
||||
} else {
|
||||
scopeEl.textContent = `${partnerDetailYear}年${partnerDetailMonth}月`;
|
||||
allBtn.textContent = '全部';
|
||||
allBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
function switchPartnerDetailScope(offset) {
|
||||
if (partnerDetailScopeAll) return;
|
||||
partnerDetailMonth += offset;
|
||||
if (partnerDetailMonth > 12) { partnerDetailMonth = 1; partnerDetailYear++; }
|
||||
if (partnerDetailMonth < 1) { partnerDetailMonth = 12; partnerDetailYear--; }
|
||||
partnerDetailPage = 1;
|
||||
partnerDetailAllRecords = [];
|
||||
updatePartnerDetailNav();
|
||||
loadPartnerDetail();
|
||||
}
|
||||
|
||||
function togglePartnerDetailAll() {
|
||||
partnerDetailScopeAll = !partnerDetailScopeAll;
|
||||
partnerDetailPage = 1;
|
||||
partnerDetailAllRecords = [];
|
||||
if (partnerDetailScopeAll) {
|
||||
partnerDetailLimit = 500;
|
||||
} else {
|
||||
partnerDetailLimit = 20;
|
||||
partnerDetailYear = currentYear;
|
||||
partnerDetailMonth = currentMonth;
|
||||
}
|
||||
updatePartnerDetailNav();
|
||||
loadPartnerDetail();
|
||||
}
|
||||
|
||||
async function loadPartnerDetail(append) {
|
||||
if (partnerDetailLoading) return;
|
||||
partnerDetailLoading = true;
|
||||
|
||||
if (!append) {
|
||||
document.getElementById('partnerDetailTitle').textContent = currentPartnerName + ' 交易记录';
|
||||
document.getElementById('partnerDetailSummary').innerHTML = '<div class="ranking-empty">加载中...</div>';
|
||||
document.getElementById('partnerDetailList').innerHTML = '';
|
||||
showPage('partnerDetailPage');
|
||||
}
|
||||
|
||||
let url = API + '/api/stats/partner-records?partner=' + encodeURIComponent(currentPartnerName) + '&page=' + partnerDetailPage + '&limit=' + partnerDetailLimit;
|
||||
if (!partnerDetailScopeAll) {
|
||||
url += '&year=' + partnerDetailYear + '&month=' + partnerDetailMonth;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(API + '/api/stats/partner-records?partner=' + encodeURIComponent(currentPartnerName), { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
const res = await fetch(url, { headers: headers() });
|
||||
if (!res.ok) { partnerDetailLoading = false; return; }
|
||||
const data = await res.json();
|
||||
document.getElementById('partnerDetailSummary').innerHTML = `
|
||||
<div style="display:flex;justify-content:space-around;flex-wrap:wrap;gap:8px">
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px">${data.dispatch_count}</div>
|
||||
<div class="daily-summary-label">总派单数</div>
|
||||
|
||||
if (!append) {
|
||||
document.getElementById('partnerDetailSummary').innerHTML = `
|
||||
<div style="display:flex;justify-content:space-around;flex-wrap:wrap;gap:8px">
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px">${data.dispatch_count}</div>
|
||||
<div class="daily-summary-label">总派单数</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#e67e22">${data.spread_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">陪玩总收入</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#27ae60">${data.rebate_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">总返点收入</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#3498db">${data.other_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">红包及其他</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#e67e22">${data.spread_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">陪玩总收入</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#27ae60">${data.rebate_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">总返点收入</div>
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<div class="daily-summary-amount" style="font-size:24px;color:#3498db">${data.other_income.toFixed(2)}</div>
|
||||
<div class="daily-summary-label">红包及其他</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
partnerDetailAllRecords = data.records || [];
|
||||
setupPartnerDetailScroll();
|
||||
} else {
|
||||
partnerDetailAllRecords = partnerDetailAllRecords.concat(data.records || []);
|
||||
}
|
||||
partnerDetailTotalCount = data.totalCount || 0;
|
||||
|
||||
const container = document.getElementById('partnerDetailList');
|
||||
if (data.records.length === 0) {
|
||||
const moreBtn = document.getElementById('partnerDetailMore');
|
||||
|
||||
if (partnerDetailAllRecords.length === 0) {
|
||||
container.innerHTML = '<div class="ranking-empty">暂无交易记录</div>';
|
||||
if (moreBtn) moreBtn.style.display = 'none';
|
||||
partnerDetailLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const grouped = {};
|
||||
data.records.forEach(r => {
|
||||
partnerDetailAllRecords.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 };
|
||||
@@ -517,5 +738,39 @@ async function loadPartnerDetail() {
|
||||
html += '</div>';
|
||||
}
|
||||
container.innerHTML = html;
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
if (moreBtn) {
|
||||
if (partnerDetailAllRecords.length < partnerDetailTotalCount) {
|
||||
moreBtn.style.display = 'block';
|
||||
moreBtn.textContent = '加载中... (' + partnerDetailAllRecords.length + '/' + partnerDetailTotalCount + ')';
|
||||
} else {
|
||||
moreBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
partnerDetailLoading = false;
|
||||
} catch (e) {
|
||||
partnerDetailLoading = false;
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function setupPartnerDetailScroll() {
|
||||
if (partnerDetailScrollHandler) {
|
||||
window.removeEventListener('scroll', partnerDetailScrollHandler);
|
||||
}
|
||||
partnerDetailScrollHandler = function() {
|
||||
if (partnerDetailLoading) return;
|
||||
if (partnerDetailScopeAll && partnerDetailAllRecords.length >= partnerDetailTotalCount) {
|
||||
window.removeEventListener('scroll', partnerDetailScrollHandler);
|
||||
return;
|
||||
}
|
||||
const scrollY = window.scrollY;
|
||||
const windowHeight = window.innerHeight;
|
||||
const docHeight = document.documentElement.scrollHeight;
|
||||
if (scrollY + windowHeight >= docHeight - 200) {
|
||||
partnerDetailPage++;
|
||||
loadPartnerDetail(true);
|
||||
}
|
||||
};
|
||||
window.addEventListener('scroll', partnerDetailScrollHandler);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user