Initial commit - Zhangmenu Django project
A menu management application built with Django, featuring: - Dish management with cover images - Order system with party dates and participants - REST API with Django REST Framework - Bootstrap-styled frontend Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
// 统计页面模块
|
||||
|
||||
const StatsPage = {
|
||||
// 初始化
|
||||
init() {
|
||||
this.loadStats();
|
||||
},
|
||||
|
||||
// 加载统计数据
|
||||
async loadStats() {
|
||||
try {
|
||||
const [dishesRes, ordersRes] = await Promise.all([
|
||||
fetch(`${API_BASE}/dishes/`),
|
||||
fetch(`${API_BASE}/orders/`)
|
||||
]);
|
||||
|
||||
const allDishes = await dishesRes.json();
|
||||
const allOrders = await ordersRes.json();
|
||||
|
||||
// 计算菜品出现次数
|
||||
const dishCompletedCounts = {};
|
||||
allOrders
|
||||
.filter(o => o.status === 'completed')
|
||||
.forEach(order => {
|
||||
(order.dishes_detail || []).forEach(dish => {
|
||||
dishCompletedCounts[dish.id] = (dishCompletedCounts[dish.id] || 0) + 1;
|
||||
});
|
||||
});
|
||||
|
||||
AppState.setDishCompletedCounts(dishCompletedCounts);
|
||||
|
||||
// 统计数据
|
||||
const totalDishes = allDishes.length;
|
||||
const completedOrders = allOrders.filter(o => o.status === 'completed').length;
|
||||
const inProgressOrders = allOrders.filter(o => o.status === 'in_progress').length;
|
||||
|
||||
// 统计参与人员
|
||||
const participantCounts = {};
|
||||
allOrders.forEach(order => {
|
||||
(order.participants || []).forEach(p => {
|
||||
participantCounts[p] = (participantCounts[p] || 0) + 1;
|
||||
});
|
||||
});
|
||||
const uniqueParticipants = Object.keys(participantCounts).length;
|
||||
|
||||
// 渲染统计卡片
|
||||
this.renderStatCards({ totalDishes, completedOrders, inProgressOrders, uniqueParticipants });
|
||||
|
||||
// 渲染菜品排行榜
|
||||
this.renderDishRank(allDishes, dishCompletedCounts);
|
||||
|
||||
} catch (e) {
|
||||
console.error('加载统计失败:', e);
|
||||
}
|
||||
},
|
||||
|
||||
// 渲染统计卡片
|
||||
renderStatCards(stats) {
|
||||
const container = document.getElementById('stats-cards');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, var(--primary-soft), #fef3c7); color: var(--primary);">
|
||||
<i class="bi bi-book"></i>
|
||||
</div>
|
||||
<div class="stat-value">${stats.totalDishes}</div>
|
||||
<div class="stat-label">菜品总数</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: var(--secondary-soft); color: var(--secondary);">
|
||||
<i class="bi bi-check-circle"></i>
|
||||
</div>
|
||||
<div class="stat-value">${stats.completedOrders}</div>
|
||||
<div class="stat-label">已完成聚会</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: var(--accent-soft); color: #b45309;">
|
||||
<i class="bi bi-clock"></i>
|
||||
</div>
|
||||
<div class="stat-value">${stats.inProgressOrders}</div>
|
||||
<div class="stat-label">进行中</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
|
||||
// 渲染菜品排行榜
|
||||
renderDishRank(dishes, counts) {
|
||||
const container = document.getElementById('dish-rank-list');
|
||||
if (!container) return;
|
||||
|
||||
// 按出现次数排序
|
||||
const dishesWithCounts = dishes.map(dish => ({
|
||||
...dish,
|
||||
orderCount: counts[dish.id] || 0
|
||||
})).filter(d => d.orderCount > 0)
|
||||
.sort((a, b) => b.orderCount - a.orderCount);
|
||||
|
||||
if (dishesWithCounts.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="empty-rank">
|
||||
<i class="bi bi-trophy"></i>
|
||||
<p>暂无数据</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = dishesWithCounts.slice(0, 10).map((dish, index) => {
|
||||
let rankClass = 'default';
|
||||
if (index === 0) rankClass = 'gold';
|
||||
else if (index === 1) rankClass = 'silver';
|
||||
else if (index === 2) rankClass = 'bronze';
|
||||
|
||||
return `
|
||||
<div class="rank-item">
|
||||
<span class="rank-number ${rankClass}">${index + 1}</span>
|
||||
<img class="rank-img" src="${dish.cover_image || getDefaultImage()}" alt="${dish.name}">
|
||||
<div class="rank-info">
|
||||
<div class="rank-name">${dish.name}</div>
|
||||
<div class="rank-count">出现 ${dish.orderCount} 次</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
window.StatsPage = StatsPage;
|
||||
Reference in New Issue
Block a user