Skip to content
Snippets Groups Projects
Commit 7b896e3a authored by Yeying's avatar Yeying
Browse files

Refactored code for a complete and functional web interface

parent e7118186
No related branches found
No related tags found
1 merge request!1Merge Lei's Flask Website into main Branch
......@@ -50,7 +50,7 @@ body {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* 头部区域样式 */
/* Header section styles */
.header-section {
display: flex;
justify-content: space-between;
......@@ -65,7 +65,7 @@ body {
font-weight: bold;
}
/* 刷新控制样式 */
/* Refresh control styles */
.refresh-controls {
display: flex;
align-items: center;
......@@ -91,7 +91,16 @@ body {
font-size: 0.9rem;
}
/* 统计摘要样式 */
/* Filter and Statistics Section */
.filter-section {
margin-bottom: 2rem;
}
.date-filter {
margin-bottom: 2.5rem; /* Increase space between filter and stats */
}
/* Statistics summary styles */
.stats-summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
......@@ -101,10 +110,16 @@ body {
.stat-box {
background-color: white;
padding: 1rem;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
text-align: center;
transition: transform 0.2s ease;
}
.stat-box:hover {
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(0,0,0,0.15);
}
.stat-box h3 {
......@@ -120,7 +135,7 @@ body {
margin: 0;
}
/* 图表区域样式 */
/* Chart area styles */
.charts-container {
margin-top: 20px;
width: 100%;
......@@ -133,11 +148,16 @@ body {
.chart-container {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-radius: 12px;
padding: 25px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
width: 100%;
height: 400px;
transition: box-shadow 0.3s ease;
}
.chart-container:hover {
box-shadow: 0 6px 20px rgba(0,0,0,0.15);
}
.chart-container h3 {
......@@ -151,7 +171,7 @@ body {
height: 300px !important;
}
/* 详细记录列表样式 */
/* Detailed records list styles */
.returns-list-container {
margin-top: 2rem;
}
......
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
// 获取所有产品图片
// Global variables
let productChart, hourlyChart;
let currentFilter = 'year';
// Execute when page is loaded
document.addEventListener('DOMContentLoaded', async function() {
await initCharts();
await loadDefaultData();
setupEventListeners();
});
// Initialize all event listeners
function setupEventListeners() {
// Add refresh button event listener
document.getElementById('refreshBtn').addEventListener('click', function() {
loadDefaultData();
});
// Add date filter change listener
document.getElementById('dateFilter').addEventListener('change', function(e) {
currentFilter = e.target.value;
filterByDate(currentFilter);
});
// Initialize product images error handling
const productImages = document.querySelectorAll('.product-image');
// 为每个图片添加错误处理
productImages.forEach(img => {
img.onerror = function() {
this.src = '/static/products/default.jpg';
};
});
}
initCharts();
filterByDate('today'); // 默认显示今日数据
// 添加刷新按钮事件监听
document.getElementById('refreshBtn').addEventListener('click', function() {
refreshData();
// 重置倒计时
countdown = 30 * 60;
});
});
// 设置倒计时刷新
let countdown = 30 * 60; // 30分钟
let currentFilter = 'today'; // 保存当前的筛选条件
// Load default year data
async function loadDefaultData() {
const dateFilter = document.getElementById('dateFilter');
dateFilter.value = 'year';
await filterByDate('year');
}
function updateCountdown() {
const minutes = Math.floor(countdown / 60);
const seconds = countdown % 60;
const timeString = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
const countdownElements = document.querySelectorAll('#countdown');
countdownElements.forEach(element => {
element.textContent = timeString;
});
countdown--;
// Date filtering functions
async function filterByDate(value) {
const today = new Date();
let startDate, endDate;
if (countdown < 0) {
countdown = 30 * 60; // 重置为30分钟
refreshData(); // 刷新数据
switch(value) {
case 'year':
startDate = new Date(today.getFullYear(), 0, 1);
endDate = new Date(today.getFullYear(), 11, 31, 23, 59, 59);
break;
case 'today':
startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59);
break;
case 'yesterday':
startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1, 23, 59, 59);
break;
case 'week':
startDate = new Date(today.setDate(today.getDate() - today.getDay()));
endDate = new Date(today.setDate(today.getDate() + 6));
endDate.setHours(23, 59, 59);
break;
case 'month':
startDate = new Date(today.getFullYear(), today.getMonth(), 1);
endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0, 23, 59, 59);
break;
}
}
// 每秒更新倒计时
setInterval(updateCountdown, 1000);
let productChart, hourlyChart;
await fetchData(startDate, endDate);
}
function initCharts() {
async function initCharts() {
const productCtx = document.getElementById('productChart').getContext('2d');
const hourlyCtx = document.getElementById('hourlyChart').getContext('2d');
......@@ -105,14 +127,14 @@ function initCharts() {
});
}
// 更新统计数据
// Update stats data
function updateStats(data) {
document.getElementById('totalReturns').textContent = data.total_returns;
document.getElementById('totalPoints').textContent = data.total_points;
document.getElementById('activeUsers').textContent = data.active_users;
}
// 更新图表数据
// Update chart data
function updateCharts(data) {
// Update product chart
const productLabels = Object.keys(data.product_stats);
......@@ -123,10 +145,10 @@ function updateCharts(data) {
productChart.update();
// Update weekly trend chart
const weeklyData = [0, 0, 0, 0, 0, 0, 0]; // Initialize array for 7 days
const weeklyData = [0, 0, 0, 0, 0, 0, 0];
data.returns.forEach(return_item => {
const date = new Date(return_item.return_date);
const dayOfWeek = date.getDay(); // 0 = Sunday, 1 = Monday, etc.
const dayOfWeek = date.getDay();
weeklyData[dayOfWeek]++;
});
......@@ -134,119 +156,16 @@ function updateCharts(data) {
hourlyChart.update();
}
// Date filtering functions
function filterByDate(value) {
const today = new Date();
let startDate, endDate;
switch(value) {
case 'today':
startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59);
break;
case 'yesterday':
startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1, 23, 59, 59);
break;
case 'week':
const weekStart = new Date(today);
weekStart.setDate(today.getDate() - today.getDay());
startDate = new Date(weekStart.getFullYear(), weekStart.getMonth(), weekStart.getDate());
endDate = new Date(weekStart.getFullYear(), weekStart.getMonth(), weekStart.getDate() + 6, 23, 59, 59);
break;
case 'month':
startDate = new Date(today.getFullYear(), today.getMonth(), 1);
endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0, 23, 59, 59);
break;
case 'quarter1':
startDate = new Date(today.getFullYear(), 0, 1);
endDate = new Date(today.getFullYear(), 2, 31, 23, 59, 59);
break;
case 'quarter2':
startDate = new Date(today.getFullYear(), 3, 1);
endDate = new Date(today.getFullYear(), 5, 30, 23, 59, 59);
break;
case 'quarter3':
startDate = new Date(today.getFullYear(), 6, 1);
endDate = new Date(today.getFullYear(), 8, 30, 23, 59, 59);
break;
case 'quarter4':
startDate = new Date(today.getFullYear(), 9, 1);
endDate = new Date(today.getFullYear(), 11, 31, 23, 59, 59);
break;
async function fetchData(startDate, endDate) {
try {
const response = await fetch(`/returns?start_date=${startDate.toISOString()}&end_date=${endDate.toISOString()}`);
const data = await response.json();
updateStats(data);
updateCharts(data);
updateTable(data.returns);
} catch (error) {
console.error('Error fetching data:', error);
}
fetchData(startDate, endDate);
}
function filterByCustomRange() {
const startDate = new Date(document.getElementById('startDate').value);
const endDate = new Date(document.getElementById('endDate').value);
fetchData(startDate, endDate);
}
function fetchData(startDate, endDate) {
fetch(`/returns?start_date=${startDate.toISOString()}&end_date=${endDate.toISOString()}`)
.then(response => response.json())
.then(data => {
updateStats(data);
updateCharts(data);
updateTable(data.returns);
})
.catch(error => console.error('Error:', error));
}
// Date picker modal functions
function showDatePickerModal() {
const modal = document.getElementById('datePickerModal');
modal.style.display = 'block';
// Set default dates
const today = new Date();
const lastMonth = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
document.getElementById('modalStartDate').value = lastMonth.toISOString().split('T')[0];
document.getElementById('modalEndDate').value = today.toISOString().split('T')[0];
}
function closeDatePickerModal() {
const modal = document.getElementById('datePickerModal');
modal.style.display = 'none';
}
function selectPresetRange(preset) {
const today = new Date();
let startDate = new Date();
switch(preset) {
case 'last7days':
startDate.setDate(today.getDate() - 7);
break;
case 'last30days':
startDate.setDate(today.getDate() - 30);
break;
case 'last90days':
startDate.setDate(today.getDate() - 90);
break;
case 'lastYear':
startDate.setFullYear(today.getFullYear() - 1);
break;
}
document.getElementById('modalStartDate').value = startDate.toISOString().split('T')[0];
document.getElementById('modalEndDate').value = today.toISOString().split('T')[0];
}
function applyDateRange() {
const startDate = new Date(document.getElementById('modalStartDate').value);
const endDate = new Date(document.getElementById('modalEndDate').value);
// Set time to start and end of day
startDate.setHours(0, 0, 0, 0);
endDate.setHours(23, 59, 59, 999);
fetchData(startDate, endDate);
closeDatePickerModal();
}
// Update table with data
......@@ -273,98 +192,7 @@ function updateTable(returns) {
<td>${return_item.product_id}</td>
<td>${return_item.reward_value}</td>
<td>${return_item.return_date}</td>
<td>
<button class="action-btn view-btn" onclick="toggleDetails(this)">View</button>
<button class="action-btn details-btn" onclick="showDetailsModal(this)" style="display: none;">Details</button>
</td>
`;
tbody.appendChild(row);
const detailsRow = document.createElement('tr');
detailsRow.className = 'details-row';
detailsRow.style.display = 'none';
detailsRow.innerHTML = `
<td colspan="6">
<div class="details-content">
<p><strong>ID:</strong> ${return_item.id}</p>
<p><strong>Customer ID:</strong> ${return_item.customer_id}</p>
<p><strong>Product ID:</strong> ${return_item.product_id}</p>
<p><strong>Reward Value:</strong> ${return_item.reward_value}</p>
<p><strong>Return Date:</strong> ${return_item.return_date}</p>
</div>
</td>
`;
tbody.appendChild(detailsRow);
});
}
// 刷新数据
function refreshData() {
// 根据当前的筛选条件重新获取数据
switch(currentFilter) {
case 'today':
filterByDate('today');
break;
case 'week':
filterByDate('week');
break;
case 'month':
filterByDate('month');
break;
case 'custom':
const customDate = document.getElementById('customDate').value;
if (customDate) {
filterByDate(customDate);
}
break;
}
}
// Table interaction functions
function toggleDetails(button) {
const row = button.closest('tr');
const detailsRow = row.nextElementSibling;
const detailsBtn = row.querySelector('.details-btn');
if (detailsRow.style.display === 'none') {
detailsRow.style.display = 'table-row';
detailsBtn.style.display = 'inline-block';
button.textContent = 'Hide';
} else {
detailsRow.style.display = 'none';
detailsBtn.style.display = 'none';
button.textContent = 'View';
}
}
// Modal functions
const modal = document.getElementById('detailsModal');
const closeBtn = document.querySelector('.close');
function showDetailsModal(button) {
const row = button.closest('tr');
const detailsContent = row.nextElementSibling.querySelector('.details-content').innerHTML;
document.getElementById('modalContent').innerHTML = detailsContent;
modal.style.display = 'block';
}
// Close modal when clicking the close button
closeBtn.onclick = function() {
modal.style.display = 'none';
}
// Close modal when clicking outside
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = 'none';
}
}
// Add double-click handler for rows
document.querySelectorAll('.returns-table tbody tr').forEach(row => {
row.addEventListener('dblclick', function() {
const detailsContent = this.nextElementSibling.querySelector('.details-content').innerHTML;
document.getElementById('modalContent').innerHTML = detailsContent;
modal.style.display = 'block';
});
});
......@@ -31,14 +31,11 @@
<div class="filter-group">
<label>Date Range:</label>
<select id="dateFilter" onchange="filterByDate(this.value)">
<option value="year">This Year</option>
<option value="today">Current Day</option>
<option value="yesterday">Yesterday</option>
<option value="week">This Week</option>
<option value="month">This Month</option>
<option value="quarter1">Q1 (Jan-Mar)</option>
<option value="quarter2">Q2 (Apr-Jun)</option>
<option value="quarter3">Q3 (Jul-Sep)</option>
<option value="quarter4">Q4 (Oct-Dec)</option>
</select>
</div>
<div class="filter-group">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment