From 9765611858203d7da8684ef1bba398d3a1e9d812 Mon Sep 17 00:00:00 2001 From: Default User Date: Mon, 2 Feb 2026 13:56:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96=EF=BC=9A?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=86=E9=A1=B5=E6=9C=BA=E5=88=B6=E3=80=81?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=B0=83=E8=AF=95=E6=97=A5=E5=BF=97=E3=80=81?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BC=93=E5=AD=98=E7=AD=96=E7=95=A5=E5=92=8C?= =?UTF-8?q?WebSocket=E8=BF=9E=E6=8E=A5=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Management.html | 195 +++++++++++++++++++++++++++++++++++++++++------- Reject.js | 51 ++++--------- 2 files changed, 185 insertions(+), 61 deletions(-) diff --git a/Management.html b/Management.html index 38f97a3..527a973 100644 --- a/Management.html +++ b/Management.html @@ -698,6 +698,7 @@ +
@@ -751,6 +752,13 @@ // 货源详情相关变量 let currentSellerId = null; // 当前正在显示的卖家ID + // 分页相关变量 + let currentPage = 1; + const pageSize = 20; + let totalSupplies = 0; + let hasMoreData = true; + let isLoading = false; + // WebSocket相关变量 let ws = null; let wsReconnectAttempts = 0; @@ -762,6 +770,7 @@ // 缓存相关变量 const CACHE_KEY = 'management_stats_cache'; const CACHE_EXPIRY = 5 * 60 * 1000; // 缓存5分钟 + const MAX_CACHE_SIZE = 50; // 最大缓存条目数 // 定时刷新相关 const REFRESH_INTERVAL = 30 * 1000; // 每30秒自动刷新一次 @@ -773,6 +782,7 @@ const beforeYesterdayBtn = document.getElementById('beforeYesterdayBtn'); const weekBtn = document.getElementById('weekBtn'); const monthBtn = document.getElementById('monthBtn'); + const lastMonthBtn = document.getElementById('lastMonthBtn'); const allBtn = document.getElementById('allBtn'); const startDateInput = document.getElementById('startDate'); const endDateInput = document.getElementById('endDate'); @@ -1189,6 +1199,7 @@ beforeYesterdayBtn.addEventListener('click', () => setFilter('beforeYesterday')); weekBtn.addEventListener('click', () => setFilter('week')); monthBtn.addEventListener('click', () => setFilter('month')); + lastMonthBtn.addEventListener('click', () => setFilter('lastMonth')); allBtn.addEventListener('click', () => setFilter('all')); // 自定义时间筛选按钮 @@ -1197,13 +1208,13 @@ // 日期输入框改变时,自动清除预设筛选的active状态 startDateInput.addEventListener('change', () => { if (startDateInput.value || endDateInput.value) { - [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, allBtn].forEach(btn => btn.classList.remove('active')); + [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, lastMonthBtn, allBtn].forEach(btn => btn.classList.remove('active')); } }); endDateInput.addEventListener('change', () => { if (startDateInput.value || endDateInput.value) { - [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, allBtn].forEach(btn => btn.classList.remove('active')); + [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, lastMonthBtn, allBtn].forEach(btn => btn.classList.remove('active')); } }); @@ -1258,8 +1269,13 @@ // 重置当前卖家ID,确保切换时间筛选时显示所有货源 currentSellerId = null; + // 重置分页参数 + currentPage = 1; + hasMoreData = true; + suppliesData = []; + // 更新按钮状态 - [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, allBtn].forEach(btn => btn.classList.remove('active')); + [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, lastMonthBtn, allBtn].forEach(btn => btn.classList.remove('active')); document.getElementById(filter + 'Btn').classList.add('active'); // 清空自定义日期输入 @@ -1287,8 +1303,13 @@ // 重置当前卖家ID,确保切换时间筛选时显示所有货源 currentSellerId = null; + // 重置分页参数 + currentPage = 1; + hasMoreData = true; + suppliesData = []; + // 清除预设筛选的active状态 - [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, allBtn].forEach(btn => btn.classList.remove('active')); + [todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, lastMonthBtn, allBtn].forEach(btn => btn.classList.remove('active')); // 重新加载数据,传递自定义日期范围 loadStats('custom', startDate, endDate); @@ -1316,10 +1337,24 @@ try { const cached = localStorage.getItem(CACHE_KEY); let cache = cached ? JSON.parse(cached) : {}; + + // 添加新缓存 cache[filter] = { data: data, timestamp: Date.now() }; + + // 管理缓存大小,移除最旧的缓存条目 + const cacheEntries = Object.entries(cache); + if (cacheEntries.length > MAX_CACHE_SIZE) { + // 按时间戳排序,移除最旧的 + cacheEntries.sort((a, b) => a[1].timestamp - b[1].timestamp); + const entriesToRemove = cacheEntries.slice(0, cacheEntries.length - MAX_CACHE_SIZE); + entriesToRemove.forEach(([key]) => { + delete cache[key]; + }); + } + localStorage.setItem(CACHE_KEY, JSON.stringify(cache)); console.log('保存数据到缓存:', filter); } catch (error) { @@ -1330,6 +1365,24 @@ // 加载统计数据 async function loadStats(filter, startDate = '', endDate = '') { try { + // 先尝试从缓存获取数据,提高响应速度 + const cacheKey = filter === 'custom' ? `custom_${startDate}_${endDate}` : filter; + const cachedData = getCachedData(cacheKey); + + if (cachedData) { + // 使用缓存数据更新UI,提高响应速度 + updateStatsInfo(cachedData.stats); + renderChart(cachedData.chartData); + chartData = cachedData.chartData; + suppliesData = cachedData.suppliesData; + usersData = cachedData.usersData; + + // 只有在当前没有显示特定卖家的货源时才显示当天所有货源 + if (!currentSellerId) { + showAllSupplies(); + } + } + // 构建API请求URL,包含自定义日期参数 let url = `/api/admin/stats/supplies?filter=${filter}`; if (filter === 'custom') { @@ -1337,7 +1390,18 @@ if (endDate) url += `&endDate=${endDate}`; } - const response = await fetch(url); + const response = await fetch(url, { + method: 'GET', + timeout: 10000, + headers: { + 'Content-Type': 'application/json' + } + }); + + if (!response.ok) { + throw new Error(`HTTP错误! 状态: ${response.status}`); + } + const data = await response.json(); if (data.success) { @@ -1353,7 +1417,6 @@ usersData = data.data.usersData; // 保存到缓存,自定义日期使用特殊的缓存键 - const cacheKey = filter === 'custom' ? `custom_${startDate}_${endDate}` : filter; saveCachedData(cacheKey, data.data); // 只有在当前没有显示特定卖家的货源时才显示当天所有货源 @@ -1362,22 +1425,7 @@ } } else { console.error('加载统计数据失败:', data.message); - - // 尝试使用缓存数据 - const cacheKey = filter === 'custom' ? `custom_${startDate}_${endDate}` : filter; - const cachedData = getCachedData(cacheKey); - if (cachedData) { - updateStatsInfo(cachedData.stats); - renderChart(cachedData.chartData); - chartData = cachedData.chartData; - suppliesData = cachedData.suppliesData; - usersData = cachedData.usersData; - - // 只有在当前没有显示特定卖家的货源时才显示当天所有货源 - if (!currentSellerId && suppliesSection.style.display === 'block') { - showAllSupplies(); - } - } + // 已经尝试过使用缓存数据,这里可以不再处理 } } catch (error) { console.error('加载统计数据出错:', error); @@ -1510,7 +1558,7 @@ }); // 显示当天所有货源 - async function showAllSupplies() { + async function showAllSupplies(reset = true) { // 保存当前滚动位置和货源列表高度 const scrollPosition = window.pageYOffset || document.documentElement.scrollTop; const originalHeight = suppliesGrid.clientHeight; @@ -1518,6 +1566,13 @@ // 清空当前正在显示的卖家ID currentSellerId = null; + // 重置分页参数 + if (reset) { + currentPage = 1; + hasMoreData = true; + suppliesData = []; + } + // 构建标题 let title = '当天所有货源'; @@ -1538,6 +1593,9 @@ case 'month': title = '本月所有货源'; break; + case 'lastMonth': + title = '上月所有货源'; + break; case 'all': title = '全部货源'; break; @@ -1564,9 +1622,16 @@ suppliesSection.style.display = 'block'; } + // 如果已经没有更多数据或正在加载,直接返回 + if (!hasMoreData || isLoading) { + return; + } + + isLoading = true; + try { // 直接从API获取所有货源数据 - let url = `/api/admin/supplies?filter=${currentFilter}`; + let url = `/api/admin/supplies?filter=${currentFilter}&page=${currentPage}&pageSize=${pageSize}`; if (currentFilter === 'custom') { const startDate = startDateInput.value; const endDate = endDateInput.value; @@ -1595,13 +1660,30 @@ // 仅在数据成功返回且有变化时才更新DOM,减少视觉闪烁 if (data.success) { console.log('showAllSupplies - 数据请求成功,supplies数量:', data.data.supplies ? data.data.supplies.length : 0); + + // 更新分页信息 + if (data.data.pagination) { + totalSupplies = data.data.pagination.total; + hasMoreData = currentPage < data.data.pagination.totalPages; + } + + // 添加新数据 if (data.data.supplies && data.data.supplies.length > 0) { - renderSupplies(data.data.supplies); - } else { + suppliesData = reset ? data.data.supplies : [...suppliesData, ...data.data.supplies]; + renderSupplies(suppliesData); + currentPage++; + } else if (reset) { // 显示空状态 console.log('showAllSupplies - 没有找到货源数据'); suppliesGrid.innerHTML = '

暂无货源数据

'; } + + // 添加加载更多按钮或提示 + if (hasMoreData) { + addLoadMoreButton(); + } else if (suppliesData.length > 0) { + addNoMoreDataHint(); + } } else { console.error('加载货源数据失败:', data.message); // 只在当前不是错误状态时才显示错误信息 @@ -1616,11 +1698,72 @@ suppliesGrid.innerHTML = '

网络异常,请检查网络连接后重试

'; } } finally { + isLoading = false; // 恢复滚动位置 window.scrollTo(0, scrollPosition); } } + // 添加加载更多按钮 + function addLoadMoreButton() { + // 检查是否已经有加载更多按钮 + if (document.getElementById('loadMoreBtn')) { + return; + } + + const loadMoreBtn = document.createElement('div'); + loadMoreBtn.id = 'loadMoreBtn'; + loadMoreBtn.style.cssText = ` + text-align: center; + padding: 15px; + grid-column: 1 / -1; + `; + loadMoreBtn.innerHTML = ` + + `; + suppliesGrid.appendChild(loadMoreBtn); + } + + // 添加没有更多数据的提示 + function addNoMoreDataHint() { + // 移除加载更多按钮 + const loadMoreBtn = document.getElementById('loadMoreBtn'); + if (loadMoreBtn) { + loadMoreBtn.remove(); + } + + // 检查是否已经有提示 + if (document.getElementById('noMoreDataHint')) { + return; + } + + const hint = document.createElement('div'); + hint.id = 'noMoreDataHint'; + hint.style.cssText = ` + text-align: center; + padding: 15px; + color: #999; + grid-column: 1 / -1; + `; + hint.textContent = '没有更多数据了'; + suppliesGrid.appendChild(hint); + } + + // 加载更多货源 + function loadMoreSupplies() { + showAllSupplies(false); + } + // 显示指定卖家的货源 async function showSuppliesBySeller(sellerId) { // 保存当前正在显示的卖家ID diff --git a/Reject.js b/Reject.js index 300f8eb..51b59bd 100644 --- a/Reject.js +++ b/Reject.js @@ -1965,9 +1965,14 @@ app.get('/api/admin/stats/supplies', async (req, res) => { const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); timeCondition = `AND created_at >= '${weekAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; } else if (filter === 'month') { - // 本月 - const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); - timeCondition = `AND created_at >= '${monthAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; + // 本月(从本月1日到当前日期) + const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); + timeCondition = `AND created_at >= '${monthStart.toISOString().slice(0, 19).replace('T', ' ')}'`; + } else if (filter === 'lastMonth') { + // 上个月(从上月1日到上月最后一天) + const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1); + const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999); + timeCondition = `AND created_at >= '${lastMonthStart.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at <= '${lastMonthEnd.toISOString().slice(0, 19).replace('T', ' ')}'`; } else if (filter === 'custom') { // 自定义时间范围 const { startDate, endDate } = req.query; @@ -2012,12 +2017,6 @@ app.get('/api/admin/stats/supplies', async (req, res) => { connection.release(); - // 调试日志:检查API返回数据结构 - console.log('stats API返回的chartData:', chartData); - console.log('stats API返回的totalSupplies:', totalSupplies); - console.log('stats API返回的totalUsers:', totalUsers); - console.log('stats API返回的avgPerUser:', avgPerUser); - sendResponse(res, true, { chartData, stats: { @@ -2064,9 +2063,14 @@ app.get('/api/admin/supplies', async (req, res) => { const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); timeCondition = `AND created_at >= '${weekAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; } else if (filter === 'month') { - // 本月 - const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); - timeCondition = `AND created_at >= '${monthAgo.toISOString().slice(0, 19).replace('T', ' ')}'`; + // 本月(从本月1日到当前日期) + const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); + timeCondition = `AND created_at >= '${monthStart.toISOString().slice(0, 19).replace('T', ' ')}'`; + } else if (filter === 'lastMonth') { + // 上个月(从上月1日到上月最后一天) + const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1); + const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999); + timeCondition = `AND created_at >= '${lastMonthStart.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at <= '${lastMonthEnd.toISOString().slice(0, 19).replace('T', ' ')}'`; } else if (filter === 'custom') { // 自定义时间范围 const { startDate, endDate } = req.query; @@ -2107,23 +2111,6 @@ app.get('/api/admin/supplies', async (req, res) => { connection.release(); - // 调试日志:检查查询结果中是否包含product_log字段 - console.log('查询到的货源数量:', supplies.length); - if (supplies.length > 0) { - console.log('第一个货源的所有字段和值:', JSON.stringify(supplies[0], null, 2)); - console.log('第一个货源的字段:', Object.keys(supplies[0])); - console.log('第一个货源是否包含product_log字段:', 'product_log' in supplies[0]); - if ('product_log' in supplies[0]) { - console.log('第一个货源的product_log值:', supplies[0].product_log); - console.log('第一个货源的product_log类型:', typeof supplies[0].product_log); - } else { - console.log('第一个货源不包含product_log字段,可能的原因:'); - console.log('1. 数据库中products表可能没有product_log字段'); - console.log('2. 字段名称可能拼写错误'); - console.log('3. 查询语句可能有问题'); - } - } - sendResponse(res, true, { supplies }, '获取货源列表成功'); } catch (error) { console.error('获取货源列表失败:', error.message); @@ -2159,9 +2146,6 @@ app.post('/api/supplies/log', async (req, res) => { ); userLoginConnection.release(); - // 打印查询结果,查看是否包含company和organization字段 - console.log('查询到的personnel信息:', personnelResult[0]); - // 自动计算变更字段 let calculatedChangedFields = changedFields; if (operationEvent.includes('编辑') && originalData && modifiedData && !changedFields) { @@ -2199,9 +2183,6 @@ app.post('/api/supplies/log', async (req, res) => { // 如果找到了员工信息,使用真实的员工数据 if (personnelResult.length > 0) { const personnelInfo = personnelResult[0]; - // 检查personnelInfo中的字段名,确保使用正确的字段名 - console.log('personnelInfo的所有字段:', Object.keys(personnelInfo)); - // 使用正确的字段名:managercompany(公司)、managerdepartment(部门)、organization(组织) logData.tracompany = personnelInfo.managercompany || personnelInfo.managerCompany || personnelInfo.MANAGERCOMPANY || ''; logData.tradepartment = personnelInfo.managerdepartment || personnelInfo.managerDepartment || personnelInfo.MANAGERDEPARTMENT || '';