Browse Source

优化Management.html页面,添加防重复点击、数据缓存、增量渲染、分页和虚拟滚动功能

Boss3
Default User 1 month ago
parent
commit
06cc8aa248
  1. 883
      Management.html

883
Management.html

@ -747,6 +747,23 @@
let suppliesData = [];
let usersData = [];
let chartData = [];
let isLoading = false; // 防重复点击标志
// 缓存相关变量
let suppliesCache = {}; // 货源数据缓存
let cacheExpiry = 5 * 60 * 1000; // 缓存过期时间(5分钟)
// 分页相关变量
let currentPage = 1; // 当前页码
let pageSize = 20; // 每页显示数量
let totalItems = 0; // 总数据量
let totalPages = 0; // 总页数
// 虚拟滚动相关变量
let virtualScrollEnabled = false; // 是否启用虚拟滚动
let itemHeight = 600; // 估计的卡片高度
let visibleCount = 5; // 可见区域显示的卡片数量
let bufferCount = 2; // 缓冲区卡片数量
// 货源详情相关变量
let currentSellerId = null; // 当前正在显示的卖家ID
@ -795,6 +812,7 @@
checkLoginAndRole();
initEventListeners();
optimizeEventListeners(); // 优化事件监听器
initWebSocket();
loadStats(currentFilter);
@ -1253,6 +1271,12 @@
// 设置筛选条件
function setFilter(filter) {
// 防重复点击检查
if (isLoading) {
console.log('数据正在加载中,请勿重复点击');
return;
}
currentFilter = filter;
// 重置当前卖家ID,确保切换时间筛选时显示所有货源
@ -1272,6 +1296,12 @@
// 应用自定义时间筛选
function applyCustomFilter() {
// 防重复点击检查
if (isLoading) {
console.log('数据正在加载中,请勿重复点击');
return;
}
const startDate = startDateInput.value;
const endDate = endDateInput.value;
@ -1327,9 +1357,72 @@
}
}
// 封装API调用函数
async function fetchSuppliesData(filter, startDate = '', endDate = '', sellerId = null) {
// 构建缓存键
const cacheKey = sellerId ?
`seller_${sellerId}_${filter}_${startDate}_${endDate}` :
`${filter}_${startDate}_${endDate}`;
// 检查缓存
if (suppliesCache[cacheKey] && (Date.now() - suppliesCache[cacheKey].timestamp < cacheExpiry)) {
console.log('使用内存缓存数据:', cacheKey);
return suppliesCache[cacheKey].data;
}
try {
// 构建API请求URL
let url = `/api/admin/supplies?filter=${filter}`;
if (sellerId) url += `&sellerId=${sellerId}`;
if (filter === 'custom') {
if (startDate) url += `&startDate=${startDate}`;
if (endDate) url += `&endDate=${endDate}`;
}
console.log('fetchSuppliesData - 正在请求API:', 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) {
// 保存到内存缓存
suppliesCache[cacheKey] = {
data: data.data.supplies || [],
timestamp: Date.now()
};
return suppliesCache[cacheKey].data;
} else {
console.error('加载货源数据失败:', data.message);
return [];
}
} catch (error) {
console.error('fetchSuppliesData - 加载货源数据出错:', error);
return [];
}
}
// 加载统计数据
async function loadStats(filter, startDate = '', endDate = '') {
// 防重复点击检查
if (isLoading) {
console.log('数据正在加载中,请勿重复点击');
return;
}
try {
isLoading = true;
// 构建API请求URL,包含自定义日期参数
let url = `/api/admin/stats/supplies?filter=${filter}`;
if (filter === 'custom') {
@ -1397,6 +1490,8 @@
showAllSupplies();
}
}
} finally {
isLoading = false;
}
}
@ -1518,6 +1613,9 @@
// 清空当前正在显示的卖家ID
currentSellerId = null;
// 重置页码
currentPage = 1;
// 构建标题
let title = '当天所有货源';
@ -1565,50 +1663,23 @@
}
try {
// 直接从API获取所有货源数据
let url = `/api/admin/supplies?filter=${currentFilter}`;
if (currentFilter === 'custom') {
const startDate = startDateInput.value;
const endDate = endDateInput.value;
if (startDate) url += `&startDate=${startDate}`;
if (endDate) url += `&endDate=${endDate}`;
}
// 使用封装的API调用函数获取数据
const startDate = currentFilter === 'custom' ? startDateInput.value : '';
const endDate = currentFilter === 'custom' ? endDateInput.value : '';
console.log('showAllSupplies - 正在请求API:', url);
const response = await fetch(url, {
method: 'GET',
timeout: 10000, // 添加10秒超时设置
headers: {
'Content-Type': 'application/json'
}
});
const supplies = await fetchSuppliesData(currentFilter, startDate, endDate);
// 检查响应状态
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
}
// 更新总数据量和总页数
totalItems = supplies.length;
totalPages = Math.ceil(totalItems / pageSize);
const data = await response.json();
console.log('showAllSupplies - 数据请求成功,总数量:', totalItems, '总页数:', totalPages);
console.log('showAllSupplies - API返回数据:', data);
// 渲染当前页数据
renderSuppliesWithPagination(supplies);
// 仅在数据成功返回且有变化时才更新DOM,减少视觉闪烁
if (data.success) {
console.log('showAllSupplies - 数据请求成功,supplies数量:', data.data.supplies ? data.data.supplies.length : 0);
if (data.data.supplies && data.data.supplies.length > 0) {
renderSupplies(data.data.supplies);
} else {
// 显示空状态
console.log('showAllSupplies - 没有找到货源数据');
suppliesGrid.innerHTML = '<p style="text-align: center; color: #666; grid-column: 1 / -1;">暂无货源数据</p>';
}
} else {
console.error('加载货源数据失败:', data.message);
// 只在当前不是错误状态时才显示错误信息
if (!suppliesGrid.innerHTML.includes('加载失败') && !suppliesGrid.innerHTML.includes('网络异常')) {
suppliesGrid.innerHTML = '<p style="text-align: center; color: #666; grid-column: 1 / -1;">加载货源数据失败,请稍后重试</p>';
}
}
// 添加分页控件
addPaginationControls();
} catch (error) {
console.error('加载货源数据出错:', error);
// 只在当前不是错误状态时才显示错误信息
@ -1626,6 +1697,9 @@
// 保存当前正在显示的卖家ID
currentSellerId = sellerId;
// 重置页码
currentPage = 1;
// 获取当前筛选条件和日期范围
let startDate = '';
let endDate = '';
@ -1637,87 +1711,68 @@
endDate = endDateInput.value;
}
// 构建API请求URL,包含自定义日期参数
let url = `/api/admin/supplies?sellerId=${sellerId}&filter=${filter}`;
if (filter === 'custom') {
if (startDate) url += `&startDate=${startDate}`;
if (endDate) url += `&endDate=${endDate}`;
}
// 显示加载状态
suppliesGrid.innerHTML = '<p style="text-align: center; color: #666; grid-column: 1 / -1;">加载中...</p>';
try {
const response = await fetch(url, {
method: 'GET',
timeout: 10000, // 添加10秒超时设置
headers: {
'Content-Type': 'application/json'
}
});
// 使用封装的API调用函数获取数据
const supplies = await fetchSuppliesData(filter, startDate, endDate, sellerId);
// 检查响应状态
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
// 更新总数据量和总页数
totalItems = supplies.length;
totalPages = Math.ceil(totalItems / pageSize);
console.log('showSuppliesBySeller - 数据请求成功,总数量:', totalItems, '总页数:', totalPages);
// 获取创建人的姓名
let creatorName = '';
// 从chartData中查找对应的nickName
const chartItem = chartData.find(item => item.sellerId === sellerId);
if (chartItem && chartItem.nickName) {
creatorName = chartItem.nickName;
}
// 如果chartData中没有,从返回的supplies中获取
else if (supplies.length > 0 && supplies[0].nickName) {
creatorName = supplies[0].nickName;
}
const data = await response.json();
// 构建标题
let title = `${sellerId} 创建的货源`;
if (creatorName) {
title = `${creatorName} (${sellerId}) 创建的货源`;
}
if (data.success) {
// 获取创建人的姓名
let creatorName = '';
// 从chartData中查找对应的nickName
const chartItem = chartData.find(item => item.sellerId === sellerId);
if (chartItem && chartItem.nickName) {
creatorName = chartItem.nickName;
}
// 如果chartData中没有,从返回的supplies中获取
else if (data.data.supplies && data.data.supplies.length > 0 && data.data.supplies[0].nickName) {
creatorName = data.data.supplies[0].nickName;
}
// 构建标题
let title = `${sellerId} 创建的货源`;
if (creatorName) {
title = `${creatorName} (${sellerId}) 创建的货源`;
}
// 添加"查看全部货源"按钮
suppliesTitle.innerHTML = `
${title}
<button id="showAllBtn" style="
margin-left: 10px;
padding: 4px 12px;
background-color: #1677ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
font-weight: normal;
">查看全部货源</button>
`;
// 为"查看全部货源"按钮添加点击事件
document.getElementById('showAllBtn').addEventListener('click', showAllSupplies);
renderSupplies(data.data.supplies);
// 只有在suppliesSection的display属性不是block时才设置,避免不必要的布局重排
if (suppliesSection.style.display !== 'block') {
suppliesSection.style.display = 'block';
// 只有在第一次显示suppliesSection时才滚动到该区域
suppliesSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
} else {
console.error('加载货源数据失败:', data.message);
suppliesGrid.innerHTML = '<p style="text-align: center; color: #666; grid-column: 1 / -1;">加载货源数据失败,请稍后重试</p>';
// 只有在suppliesSection的display属性不是block时才设置,避免不必要的布局重排
if (suppliesSection.style.display !== 'block') {
suppliesSection.style.display = 'block';
}
// 添加"查看全部货源"按钮
suppliesTitle.innerHTML = `
${title}
<button id="showAllBtn" style="
margin-left: 10px;
padding: 4px 12px;
background-color: #1677ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
font-weight: normal;
">查看全部货源</button>
`;
// 为"查看全部货源"按钮添加点击事件
document.getElementById('showAllBtn').addEventListener('click', showAllSupplies);
// 渲染当前页数据
renderSuppliesWithPagination(supplies);
// 添加分页控件
addPaginationControls();
// 只有在suppliesSection的display属性不是block时才设置,避免不必要的布局重排
if (suppliesSection.style.display !== 'block') {
suppliesSection.style.display = 'block';
// 只有在第一次显示suppliesSection时才滚动到该区域
suppliesSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
} catch (error) {
console.error('加载货源数据出错:', error);
@ -1787,7 +1842,7 @@
mediaElement = `<video src="${mediaUrl}" alt="${supply.productName}" controls class="supply-media"></video>`;
} else {
// 图片文件
mediaElement = `<img src="${mediaUrl}" alt="${supply.productName}" class="supply-media" onclick="showPreview('${mediaUrl}')">`;
mediaElement = `<img src="${mediaUrl}" alt="${supply.productName}" class="supply-media">`;
}
}
@ -1982,6 +2037,608 @@
suppliesGrid.style.overflow = '';
window.scrollTo(0, scrollPosition);
}
// 创建货源卡片
function createSupplyCard(supply) {
const card = document.createElement('div');
card.className = 'supply-card';
card.dataset.productId = supply.productId; // 添加产品ID作为数据属性
// 解析媒体URL
let mediaUrl = '';
try {
// 使用更快的JSON.parse替代复杂的字符串处理
const imageUrls = JSON.parse(supply.imageUrls || '[]');
if (imageUrls && Array.isArray(imageUrls) && imageUrls.length > 0) {
mediaUrl = imageUrls[0];
}
} catch (e) {
// 解析失败时不打印错误,避免性能影响
}
// 状态文本映射,避免switch语句
const statusMap = {
'published': '已发布',
'sold_out': '已下架',
'hidden': '已隐藏'
};
const statusClass = `status-${supply.status}`;
const statusText = statusMap[supply.status] || '待审核';
// 检查是否为视频文件(使用更简单的正则表达式)
let mediaElement = '';
if (mediaUrl) {
const isVideo = /\.(mp4|mov|avi|wmv|flv|webm|mkv)$/i.test(mediaUrl) || mediaUrl.startsWith('data:video/');
if (isVideo) {
// 视频文件
mediaElement = `<video src="${mediaUrl}" alt="${supply.productName}" controls class="supply-media"></video>`;
} else {
// 图片文件
mediaElement = `<img src="${mediaUrl}" alt="${supply.productName}" class="supply-media" onclick="showPreview('${mediaUrl}')">`;
}
}
// 第一行展示:种类|蛋黄|货源类型|产品包装|新鲜程度
const firstLineParts = [];
if (supply.category) firstLineParts.push(supply.category);
if (supply.yolk) firstLineParts.push(supply.yolk);
if (supply.sourceType) firstLineParts.push(supply.sourceType);
if (supply.producting) firstLineParts.push(supply.producting);
if (supply.freshness) firstLineParts.push(supply.freshness);
const firstLineHTML = firstLineParts.length > 0 ?
`<p class="supply-meta">${firstLineParts.join(' | ')}</p>` : '';
// 构建详细信息HTML
const detailsParts = [];
// 产品ID
if (supply.productId) {
detailsParts.push(`<p><strong>产品ID:</strong> ${supply.productId}</p>`);
}
// 规格+件数+采购价+销售价组合展示
const specifications = [];
if (supply.specification) {
if (typeof supply.specification === 'string') {
// 规格可能用逗号或中文逗号分隔
specifications.push(...supply.specification.split(/[,,]/).filter(spec => spec.trim()));
} else if (Array.isArray(supply.specification)) {
specifications.push(...supply.specification);
} else {
specifications.push(String(supply.specification));
}
}
// 处理数量、采购价、销售价
const quantities = Array.isArray(supply.quantity) ? supply.quantity :
(typeof supply.quantity === 'string' ? supply.quantity.split(',').filter(qty => qty.trim()) :
(supply.quantity ? [String(supply.quantity)] : []));
const costprices = Array.isArray(supply.costprice) ? supply.costprice :
(typeof supply.costprice === 'string' ? supply.costprice.split(',').filter(cp => cp.trim()) :
(supply.costprice ? [String(supply.costprice)] : []));
const prices = Array.isArray(supply.price) ? supply.price :
(typeof supply.price === 'string' ? supply.price.split(',').filter(p => p.trim()) :
(supply.price ? [String(supply.price)] : []));
// 计算最大长度,确保每个规格都有对应的数据
const maxLength = Math.max(specifications.length, quantities.length, costprices.length, prices.length);
// 生成组合展示HTML
if (maxLength > 0) {
const specDetails = [];
for (let i = 0; i < maxLength; i++) {
const spec = specifications[i] || '';
const quantity = quantities[i] || '';
const costprice = costprices[i] || '';
const price = prices[i] || '';
// 只显示有数据的行
if (spec || quantity || costprice || price) {
const parts = [];
if (spec) parts.push(`规格${i+1}: ${spec}`);
if (quantity) parts.push(`件数: ${quantity}件`);
if (costprice) parts.push(`采购价: ¥${costprice}`);
if (price) parts.push(`销售价: ¥${price}`);
if (parts.length > 0) {
specDetails.push(`<div class="spec-item">${parts.join(' - ')}</div>`);
}
}
}
if (specDetails.length > 0) {
detailsParts.push(`<div class="spec-section">${specDetails.join('')}</div>`);
}
}
// 商品地区
if (supply.region) {
detailsParts.push(`<p><strong>商品地区:</strong> ${supply.region}</p>`);
}
// 商品联系人和联系电话
if (supply.product_contact || supply.contact_phone) {
let contactText = '<strong>商品联系人:</strong> ';
if (supply.product_contact) {
contactText += supply.product_contact;
}
if (supply.contact_phone) {
if (supply.product_contact) {
contactText += ` ${supply.contact_phone}`;
} else {
contactText += supply.contact_phone;
}
}
detailsParts.push(`<p>${contactText}</p>`);
}
// 创建时间
detailsParts.push(`<p><strong>创建时间:</strong> ${new Date(supply.created_at).toLocaleString()}</p>`);
// 修改时间
if (supply.updated_at) {
detailsParts.push(`<p><strong>修改时间:</strong> ${new Date(supply.updated_at).toLocaleString()}</p>`);
}
// 货源描述(限制显示长度)
if (supply.description) {
const shortDesc = supply.description.length > 50 ? `${supply.description.substring(0, 50)}...` : supply.description;
detailsParts.push(`<div class="description-section"><strong>货源描述:</strong><div>${shortDesc}</div></div>`);
}
// 价格变更日志
let logHTML = `<div class="log-section"><h4>价格变更日志</h4>`;
try {
let logs = [];
let hasLogs = false;
// 检查product_log是否有值
if (supply.product_log != null && supply.product_log != undefined && supply.product_log != '') {
// 有值,处理日志
if (typeof supply.product_log === 'string') {
// 清理JSON字符串中的特殊字符
const cleanLogStr = supply.product_log.replace(/\s+/g, ' ').trim();
// 检查是否为JSON格式
if (cleanLogStr.startsWith('[') && cleanLogStr.endsWith(']')) {
try {
logs = JSON.parse(cleanLogStr);
} catch (parseError) {
// 解析失败,作为单个日志处理
logs = [supply.product_log];
}
} else {
// 不是JSON数组,作为单个日志处理
logs = [supply.product_log];
}
} else if (Array.isArray(supply.product_log)) {
logs = supply.product_log;
} else {
// 其他类型,转换为字符串数组
logs = [String(supply.product_log)];
}
hasLogs = logs.length > 0;
}
// 显示日志
if (hasLogs) {
logs.forEach((log, index) => {
logHTML += `<div class="log-item"><span class="log-badge">日志${index + 1}</span><span>${log}</span></div>`;
});
} else {
// 没有日志,显示提示
logHTML += `<div class="log-item"><span>暂无价格变更日志</span></div>`;
}
} catch (error) {
// 如果解析失败,直接显示日志内容
logHTML += `<div class="log-item"><span>${supply.product_log || '暂无价格变更日志'}</span></div>`;
}
logHTML += `</div>`;
detailsParts.push(logHTML);
// 状态
detailsParts.push(`<span class="supply-status ${statusClass}">${statusText}</span>`);
// 构建完整卡片HTML
card.innerHTML = `
<div class="supply-header">
${mediaElement}
<div class="supply-title">${supply.productName}</div>
${firstLineHTML}
</div>
<div class="supply-info">
${detailsParts.join('')}
</div>
`;
return card;
}
// 渲染带分页的货源卡片
function renderSuppliesWithPagination(supplies) {
// 计算当前页的数据范围
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const currentPageSupplies = supplies.slice(startIndex, endIndex);
if (currentPageSupplies.length === 0) {
suppliesGrid.innerHTML = '<p class="empty-state">暂无货源数据</p>';
return;
}
// 保存当前滚动位置
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
// 检查是否启用虚拟滚动
if (virtualScrollEnabled && currentPageSupplies.length > visibleCount + bufferCount * 2) {
// 使用虚拟滚动
renderWithVirtualScroll(currentPageSupplies);
} else {
// 使用增量渲染
const containerHeight = suppliesGrid.clientHeight;
suppliesGrid.style.height = `${containerHeight}px`;
suppliesGrid.style.overflow = 'hidden';
incrementalRender(currentPageSupplies);
suppliesGrid.style.height = '';
suppliesGrid.style.overflow = '';
}
// 恢复滚动位置
window.scrollTo(0, scrollPosition);
}
// 增量渲染函数
function incrementalRender(supplies) {
// 创建一个Map来存储当前页的货源
const currentSuppliesMap = new Map();
supplies.forEach(supply => {
currentSuppliesMap.set(supply.productId, supply);
});
// 存储已存在的卡片
const existingCards = new Map();
Array.from(suppliesGrid.children).forEach(child => {
if (child.classList.contains('supply-card')) {
const productId = child.dataset.productId;
if (productId) {
existingCards.set(productId, child);
}
}
});
// 创建DocumentFragment,用于构建DOM结构,减少页面重排
const fragment = document.createDocumentFragment();
// 遍历当前页的货源
supplies.forEach(supply => {
const productId = supply.productId;
if (existingCards.has(productId)) {
// 卡片已存在,检查是否需要更新
const existingCard = existingCards.get(productId);
existingCards.delete(productId); // 标记为已处理
// 简单的更新检查:比较创建时间和修改时间
const existingCreatedAt = existingCard.querySelector('.supply-info p:nth-child(6)')?.textContent;
const existingUpdatedAt = existingCard.querySelector('.supply-info p:nth-child(7)')?.textContent;
const newCreatedAt = `创建时间: ${new Date(supply.created_at).toLocaleString()}`;
const newUpdatedAt = supply.updated_at ? `修改时间: ${new Date(supply.updated_at).toLocaleString()}` : '';
if (existingCreatedAt !== newCreatedAt ||
(newUpdatedAt && existingUpdatedAt !== newUpdatedAt)) {
// 需要更新,替换为新卡片
const newCard = createSupplyCard(supply);
fragment.appendChild(newCard);
existingCard.remove();
} else {
// 不需要更新,保留原卡片
fragment.appendChild(existingCard);
}
} else {
// 卡片不存在,创建新卡片
const newCard = createSupplyCard(supply);
fragment.appendChild(newCard);
}
});
// 移除不再需要的卡片
existingCards.forEach(card => {
card.remove();
});
// 清空容器并添加新内容
suppliesGrid.innerHTML = '';
suppliesGrid.appendChild(fragment);
}
// 添加分页控件
function addPaginationControls() {
// 检查是否已经存在分页控件
const existingPagination = document.querySelector('.pagination');
if (existingPagination) {
existingPagination.remove();
}
// 如果只有一页,不需要分页控件
if (totalPages <= 1) {
return;
}
// 创建分页控件容器
const pagination = document.createElement('div');
pagination.className = 'pagination';
pagination.style.cssText = `
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
margin-top: 20px;
padding: 10px;
background-color: #f5f5f5;
border-radius: 8px;
`;
// 添加首页按钮
const firstBtn = document.createElement('button');
firstBtn.textContent = '首页';
firstBtn.style.cssText = `
padding: 6px 12px;
background-color: #1677ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
`;
firstBtn.disabled = currentPage === 1;
firstBtn.addEventListener('click', () => {
if (currentPage > 1) {
currentPage = 1;
showCurrentPageData();
}
});
pagination.appendChild(firstBtn);
// 添加上一页按钮
const prevBtn = document.createElement('button');
prevBtn.textContent = '上一页';
prevBtn.style.cssText = `
padding: 6px 12px;
background-color: #1677ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
`;
prevBtn.disabled = currentPage === 1;
prevBtn.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
showCurrentPageData();
}
});
pagination.appendChild(prevBtn);
// 添加页码按钮
const maxVisiblePages = 5;
let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
// 调整起始页码,确保显示足够的页码
if (endPage - startPage + 1 < maxVisiblePages) {
startPage = Math.max(1, endPage - maxVisiblePages + 1);
}
for (let i = startPage; i <= endPage; i++) {
const pageBtn = document.createElement('button');
pageBtn.textContent = i;
pageBtn.style.cssText = `
padding: 6px 12px;
background-color: ${i === currentPage ? '#0958d9' : '#1677ff'};
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
`;
pageBtn.addEventListener('click', () => {
if (i !== currentPage) {
currentPage = i;
showCurrentPageData();
}
});
pagination.appendChild(pageBtn);
}
// 添加下一页按钮
const nextBtn = document.createElement('button');
nextBtn.textContent = '下一页';
nextBtn.style.cssText = `
padding: 6px 12px;
background-color: #1677ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
`;
nextBtn.disabled = currentPage === totalPages;
nextBtn.addEventListener('click', () => {
if (currentPage < totalPages) {
currentPage++;
showCurrentPageData();
}
});
pagination.appendChild(nextBtn);
// 添加末页按钮
const lastBtn = document.createElement('button');
lastBtn.textContent = '末页';
lastBtn.style.cssText = `
padding: 6px 12px;
background-color: #1677ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
`;
lastBtn.disabled = currentPage === totalPages;
lastBtn.addEventListener('click', () => {
if (currentPage < totalPages) {
currentPage = totalPages;
showCurrentPageData();
}
});
pagination.appendChild(lastBtn);
// 添加页码信息
const pageInfo = document.createElement('span');
pageInfo.textContent = `第 ${currentPage} / ${totalPages} 页,共 ${totalItems} 条`;
pageInfo.style.cssText = `
margin-left: 15px;
font-size: 14px;
color: #666;
`;
pagination.appendChild(pageInfo);
// 添加到页面
suppliesSection.appendChild(pagination);
}
// 显示当前页数据
async function showCurrentPageData() {
try {
const startDate = currentFilter === 'custom' ? startDateInput.value : '';
const endDate = currentFilter === 'custom' ? endDateInput.value : '';
const supplies = await fetchSuppliesData(currentFilter, startDate, endDate, currentSellerId);
// 预加载图片
preloadImages(supplies);
// 渲染当前页数据
renderSuppliesWithPagination(supplies);
// 更新分页控件
addPaginationControls();
} catch (error) {
console.error('加载当前页数据出错:', error);
}
}
// 优化事件委托,减少事件监听器数量
function optimizeEventListeners() {
// 使用事件委托处理图片点击事件
suppliesGrid.addEventListener('click', function(e) {
const mediaElement = e.target.closest('.supply-media');
if (mediaElement) {
if (mediaElement.tagName === 'IMG') {
const mediaUrl = mediaElement.src;
showPreview(mediaUrl);
}
}
});
}
// 预加载图片,提高用户体验
function preloadImages(supplies) {
supplies.forEach(supply => {
try {
const imageUrls = JSON.parse(supply.imageUrls || '[]');
if (imageUrls && Array.isArray(imageUrls) && imageUrls.length > 0) {
const img = new Image();
img.src = imageUrls[0];
}
} catch (e) {
// 解析失败,忽略
}
});
}
// 虚拟滚动渲染函数
function renderWithVirtualScroll(supplies) {
// 清空容器
suppliesGrid.innerHTML = '';
// 设置容器样式
suppliesGrid.style.cssText = `
position: relative;
overflow-y: auto;
max-height: 800px;
`;
// 创建一个容器来存放虚拟元素
const virtualContainer = document.createElement('div');
virtualContainer.style.cssText = `
position: absolute;
top: 0;
left: 0;
width: 100%;
`;
suppliesGrid.appendChild(virtualContainer);
// 创建占位元素,模拟整个列表的高度
const placeholder = document.createElement('div');
placeholder.style.cssText = `
height: ${supplies.length * itemHeight}px;
width: 100%;
`;
virtualContainer.appendChild(placeholder);
// 存储当前渲染的元素
const renderedItems = new Map();
// 渲染可视区域内的元素
function renderVisibleItems() {
// 获取容器的滚动位置和可视区域高度
const scrollTop = suppliesGrid.scrollTop;
const containerHeight = suppliesGrid.clientHeight;
// 计算应该渲染的元素范围
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferCount);
const endIndex = Math.min(supplies.length, Math.ceil((scrollTop + containerHeight) / itemHeight) + bufferCount);
// 移除不在可视区域内的元素
renderedItems.forEach((item, index) => {
if (index < startIndex || index >= endIndex) {
item.remove();
renderedItems.delete(index);
}
});
// 渲染可视区域内的元素
for (let i = startIndex; i < endIndex; i++) {
if (!renderedItems.has(i)) {
const supply = supplies[i];
const item = createSupplyCard(supply);
item.style.cssText = `
position: absolute;
top: ${i * itemHeight}px;
left: 0;
width: 100%;
height: ${itemHeight}px;
box-sizing: border-box;
`;
virtualContainer.appendChild(item);
renderedItems.set(i, item);
}
}
}
// 初始渲染
renderVisibleItems();
// 监听滚动事件,动态渲染元素
suppliesGrid.addEventListener('scroll', renderVisibleItems);
}
</script>
</body>
</html>
Loading…
Cancel
Save