当 r.quantity 为 0 时,原代码 r.amount / r.quantity 会导致除零错误。 修复:优先使用 r.unit_price,仅在 unit_price 不存在时才计算,并添加防护。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
129 lines
7.7 KiB
JavaScript
129 lines
7.7 KiB
JavaScript
// 全局状态变量(供 app.js 引用)
|
||
var orderPage = 1, orderHasMore = true, orderLoading = false;
|
||
var otherIncomePage = 1, otherIncomeHasMore = true, otherIncomeLoading = false;
|
||
var dispatchPage = 1, dispatchHasMore = true, dispatchLoading = false;
|
||
var redEnvelopePage = 1, redEnvelopeHasMore = true, redEnvelopeLoading = false;
|
||
var milkTeaPage = 1, milkTeaHasMore = true, milkTeaLoading = false;
|
||
|
||
// 通用分页状态管理
|
||
const listConfig = {
|
||
orderList: { getPage: () => orderPage, setPage: (v) => orderPage = v, getHasMore: () => orderHasMore, setHasMore: (v) => orderHasMore = v, getLoading: () => orderLoading, setLoading: (v) => orderLoading = v, emptyMsg: '暂无接单记录', endpoint: '/api/records/orders', renderItem: renderOrderItem },
|
||
otherIncomeList: { getPage: () => otherIncomePage, setPage: (v) => otherIncomePage = v, getHasMore: () => otherIncomeHasMore, setHasMore: (v) => otherIncomeHasMore = v, getLoading: () => otherIncomeLoading, setLoading: (v) => otherIncomeLoading = v, emptyMsg: '暂无红包及其他收入记录', endpoint: '/api/records/other-income', renderItem: renderOtherIncomeItem },
|
||
dispatchList: { getPage: () => dispatchPage, setPage: (v) => dispatchPage = v, getHasMore: () => dispatchHasMore, setHasMore: (v) => dispatchHasMore = v, getLoading: () => dispatchLoading, setLoading: (v) => dispatchLoading = v, emptyMsg: '暂无派单记录', endpoint: '/api/records/dispatches', renderItem: renderDispatchItem },
|
||
redEnvelopeList: { getPage: () => redEnvelopePage, setPage: (v) => redEnvelopePage = v, getHasMore: () => redEnvelopeHasMore, setHasMore: (v) => redEnvelopeHasMore = v, getLoading: () => redEnvelopeLoading, setLoading: (v) => redEnvelopeLoading = v, emptyMsg: '暂无红包收入记录', endpoint: '/api/records/red-envelope', renderItem: renderRedEnvelopeItem },
|
||
milkTeaList: { getPage: () => milkTeaPage, setPage: (v) => milkTeaPage = v, getHasMore: () => milkTeaHasMore, setHasMore: (v) => milkTeaHasMore = v, getLoading: () => milkTeaLoading, setLoading: (v) => milkTeaLoading = v, emptyMsg: '暂无奶茶收入记录', endpoint: '/api/records/milk-tea', renderItem: renderMilkTeaItem }
|
||
};
|
||
|
||
// 通用列表加载函数
|
||
async function loadList(listId, append) {
|
||
const config = listConfig[listId];
|
||
if (!config) return;
|
||
|
||
if (config.getLoading() || (!append && !config.getHasMore())) return;
|
||
config.setLoading(true);
|
||
document.getElementById(listId.replace('List', 'Loading')).style.display = 'block';
|
||
|
||
try {
|
||
const res = await fetch(API + config.endpoint + '?page=' + config.getPage() + '&limit=20&year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
|
||
if (!res.ok) return;
|
||
const data = await res.json();
|
||
const container = document.getElementById(listId);
|
||
if (!append) container.innerHTML = '';
|
||
|
||
if (data.records.length === 0) {
|
||
if (config.getPage() === 1) container.innerHTML = '<div class="ranking-empty">' + config.emptyMsg + '</div>';
|
||
config.setHasMore(false);
|
||
} else {
|
||
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 += '<div class="order-date-group" data-date="' + date + '"><div class="order-date-label">' + date + '</div>';
|
||
records.forEach(r => {
|
||
html += config.renderItem(r);
|
||
});
|
||
html += '</div>';
|
||
}
|
||
container.insertAdjacentHTML('beforeend', html);
|
||
config.setHasMore(data.records.length >= 20);
|
||
config.setPage(config.getPage() + 1);
|
||
}
|
||
} catch (e) { console.error(e); }
|
||
|
||
config.setLoading(false);
|
||
document.getElementById(listId.replace('List', 'Loading')).style.display = 'none';
|
||
}
|
||
|
||
// 通用渲染函数
|
||
function renderOrderItem(r) {
|
||
const time = formatLocalTime(r.created_at);
|
||
return '<div class="order-item" onclick=\'openEditModal(' + JSON.stringify(r).replace(/'/g, "'") + ')\'>' +
|
||
'<div class="order-item-top"><span class="order-item-boss">' + (r.boss || '-') + '</span><span class="order-item-amount">' + (r.amount || 0).toFixed(2) + '</span></div>' +
|
||
'<div class="order-item-bottom"><span>' + (r.quantity || 0) + '单 × ' + (r.unit_price || 0).toFixed(2) + '</span><span>' + time + (r.partner ? ' · ' + r.partner : '') + '</span></div></div>';
|
||
}
|
||
|
||
function renderOtherIncomeItem(r) {
|
||
const time = formatLocalTime(r.created_at);
|
||
const displayCategory = r.category === '红包收入' ? '红包' : (r.category || '-');
|
||
return '<div class="order-item" onclick=\'openEditModal(' + JSON.stringify(r).replace(/'/g, "'") + ')\'>' +
|
||
'<div class="order-item-top"><span class="order-item-boss">' + displayCategory + (r.boss ? ' · ' + r.boss : '') + '</span><span class="order-item-amount">' + (r.amount || 0).toFixed(2) + '</span></div>' +
|
||
'<div class="order-item-bottom"><span>' + (r.source || '') + '</span><span>' + time + '</span></div></div>';
|
||
}
|
||
|
||
function renderDispatchItem(r) {
|
||
const time = formatLocalTime(r.created_at);
|
||
const unitPrice = r.unit_price || (r.quantity ? r.amount / r.quantity : 0);
|
||
return '<div class="order-item" onclick=\'openEditModal(' + JSON.stringify(r).replace(/'/g, "'") + ')\'>' +
|
||
'<div class="order-item-top"><span class="order-item-boss">' + (r.boss || '-') + '</span><span class="order-item-amount">' + (r.amount || 0).toFixed(2) + '</span></div>' +
|
||
'<div class="order-item-bottom"><span>' + (r.quantity || 0) + '单 × ' + (r.rebate || 0).toFixed(2) + '(单价' + unitPrice.toFixed(2) + ')</span><span>' + time + (r.partner ? ' · ' + r.partner : '') + '</span></div></div>';
|
||
}
|
||
|
||
function renderRedEnvelopeItem(r) {
|
||
const time = formatLocalTime(r.created_at);
|
||
return '<div class="order-item" onclick=\'openEditModal(' + JSON.stringify(r).replace(/'/g, "'") + ')\'>' +
|
||
'<div class="order-item-top"><span class="order-item-boss">红包</span><span class="order-item-amount">' + (r.amount || 0).toFixed(2) + '</span></div>' +
|
||
'<div class="order-item-bottom"><span>' + (r.source || '') + '</span><span>' + time + '</span></div></div>';
|
||
}
|
||
|
||
function renderMilkTeaItem(r) {
|
||
const time = formatLocalTime(r.created_at);
|
||
return '<div class="order-item" onclick=\'openEditModal(' + JSON.stringify(r).replace(/'/g, "'") + ')\'>' +
|
||
'<div class="order-item-top"><span class="order-item-boss">奶茶</span><span class="order-item-amount">' + (r.amount || 0).toFixed(2) + '</span></div>' +
|
||
'<div class="order-item-bottom"><span>' + (r.source || '') + '</span><span>' + time + '</span></div></div>';
|
||
}
|
||
|
||
// 滚动加载
|
||
window.addEventListener('scroll', () => {
|
||
const pageListMap = [
|
||
{ page: 'orderListPage', list: 'orderList' },
|
||
{ page: 'dispatchListPage', list: 'dispatchList' },
|
||
{ page: 'otherIncomeListPage', list: 'otherIncomeList' },
|
||
{ page: 'redEnvelopeListPage', list: 'redEnvelopeList' },
|
||
{ page: 'milkTeaListPage', list: 'milkTeaList' }
|
||
];
|
||
|
||
for (const m of pageListMap) {
|
||
if (document.getElementById(m.page).classList.contains('active')) {
|
||
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
|
||
const config = listConfig[m.list];
|
||
if (config && config.getHasMore() && !config.getLoading()) {
|
||
loadList(m.list, true);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 兼容旧接口
|
||
async function loadOrders(append) { return loadList('orderList', append); }
|
||
async function loadOtherIncome(append) { return loadList('otherIncomeList', append); }
|
||
async function loadDispatches(append) { return loadList('dispatchList', append); }
|
||
async function loadRedEnvelope(append) { return loadList('redEnvelopeList', append); }
|
||
async function loadMilkTea(append) { return loadList('milkTeaList', append); }
|