|
|
|
@ -432,6 +432,50 @@ |
|
|
|
.supply-card video { |
|
|
|
cursor: pointer; |
|
|
|
} |
|
|
|
|
|
|
|
/* 回到顶部按钮样式 */ |
|
|
|
.back-to-top { |
|
|
|
position: fixed; |
|
|
|
bottom: 30px; |
|
|
|
right: 30px; |
|
|
|
width: 40px; |
|
|
|
height: 40px; |
|
|
|
border-radius: 50%; |
|
|
|
background-color: rgba(153, 153, 153, 0.7); /* 浅灰色透明背景 */ |
|
|
|
color: #333; |
|
|
|
border: none; |
|
|
|
cursor: pointer; |
|
|
|
z-index: 1000; |
|
|
|
transition: all 0.3s ease; |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
.back-to-top-content { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
.back-to-top-text { |
|
|
|
font-size: 12px; |
|
|
|
font-weight: bold; |
|
|
|
line-height: 1; |
|
|
|
} |
|
|
|
|
|
|
|
.back-to-top:hover { |
|
|
|
background-color: rgba(102, 102, 102, 0.9); /* 加深颜色 */ |
|
|
|
transform: translateY(-3px); |
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); |
|
|
|
} |
|
|
|
|
|
|
|
.back-to-top:active { |
|
|
|
transform: translateY(0); |
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
</style> |
|
|
|
</head> |
|
|
|
<body> |
|
|
|
@ -443,6 +487,13 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 回到顶部按钮 --> |
|
|
|
<button id="backToTop" class="back-to-top" onclick="scrollToSuppliesSection()" style="display: none;"> |
|
|
|
<div class="back-to-top-content"> |
|
|
|
<span class="back-to-top-text">Top</span> |
|
|
|
</div> |
|
|
|
</button> |
|
|
|
|
|
|
|
<div class="container"> |
|
|
|
<div class="title-bar"> |
|
|
|
<div style="display: flex; align-items: center; gap: 15px;"> |
|
|
|
@ -489,7 +540,7 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="supplies-section" id="suppliesSection" style="display: none;"> |
|
|
|
<div class="supplies-section" id="suppliesSection" style="display: block;"> |
|
|
|
<h3 id="suppliesTitle">用户货源详情</h3> |
|
|
|
<div class="supplies-grid" id="suppliesGrid"> |
|
|
|
<!-- 货源卡片将动态生成 --> |
|
|
|
@ -511,9 +562,10 @@ |
|
|
|
// WebSocket相关变量 |
|
|
|
let ws = null; |
|
|
|
let wsReconnectAttempts = 0; |
|
|
|
const maxReconnectAttempts = 5; |
|
|
|
const reconnectDelay = 3000; |
|
|
|
const maxReconnectAttempts = 10; // 增加最大重连次数 |
|
|
|
const reconnectDelay = 5000; // 增加重连延迟,避免频繁重连 |
|
|
|
let wsUrl = ''; |
|
|
|
let wsConnecting = false; // 防止重复连接 |
|
|
|
|
|
|
|
// 缓存相关变量 |
|
|
|
const CACHE_KEY = 'management_stats_cache'; |
|
|
|
@ -581,7 +633,20 @@ |
|
|
|
|
|
|
|
// 初始化WebSocket连接 |
|
|
|
function initWebSocket() { |
|
|
|
// 防止重复连接 |
|
|
|
if (wsConnecting) { |
|
|
|
console.log('WebSocket连接正在建立中,请勿重复调用'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查是否已经有活跃连接 |
|
|
|
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) { |
|
|
|
console.log('WebSocket连接已存在或正在建立中,无需重新连接'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
wsConnecting = true; |
|
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; |
|
|
|
wsUrl = `${protocol}//${window.location.host}`; |
|
|
|
console.log('正在尝试连接WebSocket服务器:', wsUrl); |
|
|
|
@ -592,6 +657,8 @@ |
|
|
|
ws.close(1000, '重新连接'); |
|
|
|
} catch (error) { |
|
|
|
console.error('关闭旧WebSocket连接失败:', error); |
|
|
|
} finally { |
|
|
|
ws = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -602,6 +669,7 @@ |
|
|
|
ws.onopen = function(event) { |
|
|
|
console.log('WebSocket连接已打开'); |
|
|
|
wsReconnectAttempts = 0; // 重置重连计数器 |
|
|
|
wsConnecting = false; |
|
|
|
}; |
|
|
|
|
|
|
|
ws.onmessage = function(event) { |
|
|
|
@ -615,6 +683,8 @@ |
|
|
|
|
|
|
|
ws.onclose = function(event) { |
|
|
|
console.log('WebSocket连接已关闭:', event.code, event.reason); |
|
|
|
wsConnecting = false; |
|
|
|
ws = null; |
|
|
|
|
|
|
|
// 尝试重新连接,最多重试maxReconnectAttempts次 |
|
|
|
if (wsReconnectAttempts < maxReconnectAttempts) { |
|
|
|
@ -629,9 +699,12 @@ |
|
|
|
ws.onerror = function(error) { |
|
|
|
console.error('WebSocket错误:', error); |
|
|
|
console.error('WebSocket错误详情:', error.message); |
|
|
|
wsConnecting = false; |
|
|
|
}; |
|
|
|
} catch (error) { |
|
|
|
console.error('WebSocket连接失败:', error); |
|
|
|
wsConnecting = false; |
|
|
|
ws = null; |
|
|
|
|
|
|
|
// 尝试重新连接 |
|
|
|
if (wsReconnectAttempts < maxReconnectAttempts) { |
|
|
|
@ -650,9 +723,12 @@ |
|
|
|
const data = JSON.parse(message); |
|
|
|
console.log('处理WebSocket消息:', data); |
|
|
|
|
|
|
|
// 对于任何类型的WebSocket消息,都重新加载数据,确保实时更新 |
|
|
|
if (data.type) { |
|
|
|
console.log('收到WebSocket消息,刷新数据:', data.type); |
|
|
|
// 只有当消息类型与货源相关时,才重新加载数据 |
|
|
|
if (data.type && (data.type === 'supply_created' || data.type === 'supply_updated' || data.type === 'supply_deleted' || data.type === 'supply_status_change')) { |
|
|
|
console.log('收到货源相关WebSocket消息,刷新数据:', data.type); |
|
|
|
|
|
|
|
// 保存当前滚动位置 |
|
|
|
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop; |
|
|
|
|
|
|
|
// 重新加载统计数据 |
|
|
|
if (currentFilter === 'custom') { |
|
|
|
@ -669,8 +745,11 @@ |
|
|
|
console.log('正在显示货源详情,重新加载数据'); |
|
|
|
showSuppliesBySeller(currentSellerId); |
|
|
|
} |
|
|
|
|
|
|
|
// 恢复滚动位置 |
|
|
|
window.scrollTo(0, scrollPosition); |
|
|
|
} else { |
|
|
|
console.warn('收到未知格式的WebSocket消息'); |
|
|
|
console.warn('收到未知格式的WebSocket消息,不刷新数据:', data.type); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('解析WebSocket消息失败:', error); |
|
|
|
@ -800,6 +879,23 @@ |
|
|
|
closePreviewModal(); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 滚动事件,用于显示/隐藏回到顶部按钮 |
|
|
|
window.addEventListener('scroll', () => { |
|
|
|
const backToTopBtn = document.getElementById('backToTop'); |
|
|
|
// 当滚动距离超过300px时显示按钮 |
|
|
|
if (window.pageYOffset > 300) { |
|
|
|
backToTopBtn.style.display = 'flex'; |
|
|
|
} else { |
|
|
|
backToTopBtn.style.display = 'none'; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// 回到supplies-section顶部 |
|
|
|
function scrollToSuppliesSection() { |
|
|
|
const suppliesSection = document.getElementById('suppliesSection'); |
|
|
|
suppliesSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
|
} |
|
|
|
|
|
|
|
// 设置筛选条件 |
|
|
|
@ -814,9 +910,6 @@ |
|
|
|
startDateInput.value = ''; |
|
|
|
endDateInput.value = ''; |
|
|
|
|
|
|
|
// 隐藏货源详情 |
|
|
|
suppliesSection.style.display = 'none'; |
|
|
|
|
|
|
|
// 重新加载数据 |
|
|
|
loadStats(filter); |
|
|
|
} |
|
|
|
@ -838,9 +931,6 @@ |
|
|
|
// 清除预设筛选的active状态 |
|
|
|
[todayBtn, yesterdayBtn, beforeYesterdayBtn, weekBtn, monthBtn, allBtn].forEach(btn => btn.classList.remove('active')); |
|
|
|
|
|
|
|
// 隐藏货源详情 |
|
|
|
suppliesSection.style.display = 'none'; |
|
|
|
|
|
|
|
// 重新加载数据,传递自定义日期范围 |
|
|
|
loadStats('custom', startDate, endDate); |
|
|
|
} |
|
|
|
@ -906,6 +996,11 @@ |
|
|
|
// 保存到缓存,自定义日期使用特殊的缓存键 |
|
|
|
const cacheKey = filter === 'custom' ? `custom_${startDate}_${endDate}` : filter; |
|
|
|
saveCachedData(cacheKey, data.data); |
|
|
|
|
|
|
|
// 只有在当前没有显示特定卖家的货源时才显示当天所有货源 |
|
|
|
if (!currentSellerId) { |
|
|
|
showAllSupplies(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.error('加载统计数据失败:', data.message); |
|
|
|
|
|
|
|
@ -918,6 +1013,11 @@ |
|
|
|
chartData = cachedData.chartData; |
|
|
|
suppliesData = cachedData.suppliesData; |
|
|
|
usersData = cachedData.usersData; |
|
|
|
|
|
|
|
// 只有在当前没有显示特定卖家的货源时才显示当天所有货源 |
|
|
|
if (!currentSellerId && suppliesSection.style.display === 'block') { |
|
|
|
showAllSupplies(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
@ -932,6 +1032,11 @@ |
|
|
|
chartData = cachedData.chartData; |
|
|
|
suppliesData = cachedData.suppliesData; |
|
|
|
usersData = cachedData.usersData; |
|
|
|
|
|
|
|
// 只有在当前没有显示特定卖家的货源时才显示当天所有货源 |
|
|
|
if (!currentSellerId) { |
|
|
|
showAllSupplies(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -1045,6 +1150,118 @@ |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 显示当天所有货源 |
|
|
|
async function showAllSupplies() { |
|
|
|
// 保存当前滚动位置和货源列表高度 |
|
|
|
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop; |
|
|
|
const originalHeight = suppliesGrid.clientHeight; |
|
|
|
|
|
|
|
// 清空当前正在显示的卖家ID |
|
|
|
currentSellerId = null; |
|
|
|
|
|
|
|
// 构建标题 |
|
|
|
let title = '当天所有货源'; |
|
|
|
|
|
|
|
// 根据当前筛选条件更新标题 |
|
|
|
switch (currentFilter) { |
|
|
|
case 'today': |
|
|
|
title = '今日所有货源'; |
|
|
|
break; |
|
|
|
case 'yesterday': |
|
|
|
title = '昨日所有货源'; |
|
|
|
break; |
|
|
|
case 'beforeYesterday': |
|
|
|
title = '前日所有货源'; |
|
|
|
break; |
|
|
|
case 'week': |
|
|
|
title = '本周所有货源'; |
|
|
|
break; |
|
|
|
case 'month': |
|
|
|
title = '本月所有货源'; |
|
|
|
break; |
|
|
|
case 'all': |
|
|
|
title = '全部货源'; |
|
|
|
break; |
|
|
|
case 'custom': |
|
|
|
const startDate = startDateInput.value; |
|
|
|
const endDate = endDateInput.value; |
|
|
|
if (startDate && endDate) { |
|
|
|
title = `${startDate} 至 ${endDate} 所有货源`; |
|
|
|
} else if (startDate) { |
|
|
|
title = `${startDate} 至今所有货源`; |
|
|
|
} else if (endDate) { |
|
|
|
title = `截至 ${endDate} 所有货源`; |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
// 只有在标题变化时才更新,减少不必要的DOM操作 |
|
|
|
if (suppliesTitle.textContent !== title) { |
|
|
|
suppliesTitle.textContent = title; |
|
|
|
} |
|
|
|
|
|
|
|
// 只有在suppliesSection的display属性不是block时才设置,避免不必要的布局重排 |
|
|
|
if (suppliesSection.style.display !== 'block') { |
|
|
|
suppliesSection.style.display = 'block'; |
|
|
|
} |
|
|
|
|
|
|
|
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}`; |
|
|
|
} |
|
|
|
|
|
|
|
console.log('showAllSupplies - 正在请求API:', url); |
|
|
|
const response = await fetch(url, { |
|
|
|
method: 'GET', |
|
|
|
timeout: 10000, // 添加10秒超时设置 |
|
|
|
headers: { |
|
|
|
'Content-Type': 'application/json' |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 检查响应状态 |
|
|
|
if (!response.ok) { |
|
|
|
throw new Error(`HTTP错误! 状态: ${response.status}`); |
|
|
|
} |
|
|
|
|
|
|
|
const data = await response.json(); |
|
|
|
|
|
|
|
console.log('showAllSupplies - API返回数据:', data); |
|
|
|
|
|
|
|
// 仅在数据成功返回且有变化时才更新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>'; |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('加载货源数据出错:', error); |
|
|
|
// 只在当前不是错误状态时才显示错误信息 |
|
|
|
if (!suppliesGrid.innerHTML.includes('加载失败') && !suppliesGrid.innerHTML.includes('网络异常')) { |
|
|
|
suppliesGrid.innerHTML = '<p style="text-align: center; color: #666; grid-column: 1 / -1;">网络异常,请检查网络连接后重试</p>'; |
|
|
|
} |
|
|
|
} finally { |
|
|
|
// 恢复滚动位置 |
|
|
|
window.scrollTo(0, scrollPosition); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 显示指定卖家的货源 |
|
|
|
async function showSuppliesBySeller(sellerId) { |
|
|
|
// 保存当前正在显示的卖家ID |
|
|
|
@ -1068,8 +1285,23 @@ |
|
|
|
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); |
|
|
|
const response = await fetch(url, { |
|
|
|
method: 'GET', |
|
|
|
timeout: 10000, // 添加10秒超时设置 |
|
|
|
headers: { |
|
|
|
'Content-Type': 'application/json' |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 检查响应状态 |
|
|
|
if (!response.ok) { |
|
|
|
throw new Error(`HTTP错误! 状态: ${response.status}`); |
|
|
|
} |
|
|
|
|
|
|
|
const data = await response.json(); |
|
|
|
|
|
|
|
if (data.success) { |
|
|
|
@ -1094,43 +1326,59 @@ |
|
|
|
|
|
|
|
suppliesTitle.textContent = title; |
|
|
|
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>'; |
|
|
|
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'; |
|
|
|
suppliesSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('加载货源数据出错:', error); |
|
|
|
suppliesGrid.innerHTML = '<p style="text-align: center; color: #666; grid-column: 1 / -1;">加载货源数据失败,请刷新页面重试</p>'; |
|
|
|
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'; |
|
|
|
suppliesSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 渲染货源卡片 |
|
|
|
function renderSupplies(supplies) { |
|
|
|
console.log('renderSupplies函数被调用,supplies数量:', supplies.length); |
|
|
|
console.log('完整supplies数据:', supplies); |
|
|
|
suppliesGrid.innerHTML = ''; |
|
|
|
|
|
|
|
// 过滤掉hidden状态的货源 |
|
|
|
const filteredSupplies = supplies.filter(supply => supply.status !== 'hidden'); |
|
|
|
console.log('过滤后的supplies数量:', filteredSupplies.length); |
|
|
|
console.log('过滤后的supplies数据:', filteredSupplies); |
|
|
|
// 不过滤任何状态的货源,显示所有货源 |
|
|
|
const filteredSupplies = supplies; |
|
|
|
|
|
|
|
if (filteredSupplies.length === 0) { |
|
|
|
suppliesGrid.innerHTML = '<p style="text-align: center; color: #666; grid-column: 1 / -1;">暂无货源数据</p>'; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 保存当前滚动位置和容器高度 |
|
|
|
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop; |
|
|
|
const containerHeight = suppliesGrid.clientHeight; |
|
|
|
|
|
|
|
// 设置容器固定高度,防止清空内容时页面高度变化 |
|
|
|
suppliesGrid.style.height = `${containerHeight}px`; |
|
|
|
suppliesGrid.style.overflow = 'hidden'; |
|
|
|
|
|
|
|
// 创建DocumentFragment,用于构建DOM结构,减少页面重排 |
|
|
|
const fragment = document.createDocumentFragment(); |
|
|
|
|
|
|
|
filteredSupplies.forEach((supply, index) => { |
|
|
|
console.log(`渲染第${index + 1}个货源,ID:`, supply.productId); |
|
|
|
console.log(`该货源的product_log字段:`, supply.product_log); |
|
|
|
console.log(`该货源的product_log类型:`, typeof supply.product_log); |
|
|
|
|
|
|
|
const card = document.createElement('div'); |
|
|
|
card.className = 'supply-card'; |
|
|
|
card.style.cssText = 'border: 1px solid #e8e8e8; border-radius: 8px; padding: 15px; transition: all 0.3s;'; |
|
|
|
@ -1304,11 +1552,6 @@ |
|
|
|
</div>`; |
|
|
|
} |
|
|
|
|
|
|
|
// 产品日志展示 - 添加调试信息 |
|
|
|
console.log('当前货源的product_log字段:', supply.product_log); |
|
|
|
console.log('product_log类型:', typeof supply.product_log); |
|
|
|
console.log('product_log值是否为null:', supply.product_log === null); |
|
|
|
|
|
|
|
// 无论product_log是什么类型,都显示日志区域 |
|
|
|
detailsHTML += `<div style="margin: 10px 0;"> |
|
|
|
<h4 style="margin: 0 0 10px 0; font-size: 14px; font-weight: bold; color: #333; padding-bottom: 5px; border-bottom: 2px solid #ff4d4f;">价格变更日志</h4>`; |
|
|
|
@ -1317,43 +1560,34 @@ |
|
|
|
let logs = []; |
|
|
|
let hasLogs = false; |
|
|
|
|
|
|
|
console.log('开始处理product_log:', supply.product_log); |
|
|
|
|
|
|
|
// 检查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(); |
|
|
|
console.log('清理后的product_log:', cleanLogStr); |
|
|
|
|
|
|
|
// 检查是否为JSON格式 |
|
|
|
if (cleanLogStr.startsWith('[') && cleanLogStr.endsWith(']')) { |
|
|
|
try { |
|
|
|
logs = JSON.parse(cleanLogStr); |
|
|
|
console.log('解析成功,logs:', logs); |
|
|
|
} catch (parseError) { |
|
|
|
console.error('JSON解析失败:', parseError); |
|
|
|
// 解析失败,作为单个日志处理 |
|
|
|
logs = [supply.product_log]; |
|
|
|
console.log('作为单个日志处理,logs:', logs); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 不是JSON数组,作为单个日志处理 |
|
|
|
logs = [supply.product_log]; |
|
|
|
console.log('不是JSON数组,作为单个日志处理,logs:', logs); |
|
|
|
} |
|
|
|
} else if (Array.isArray(supply.product_log)) { |
|
|
|
logs = supply.product_log; |
|
|
|
console.log('是数组,直接使用,logs:', logs); |
|
|
|
} else { |
|
|
|
// 其他类型,转换为字符串数组 |
|
|
|
logs = [String(supply.product_log)]; |
|
|
|
console.log('转换为字符串数组,logs:', logs); |
|
|
|
} |
|
|
|
|
|
|
|
hasLogs = logs.length > 0; |
|
|
|
console.log('hasLogs:', hasLogs); |
|
|
|
} |
|
|
|
|
|
|
|
// 显示日志 |
|
|
|
@ -1419,8 +1653,18 @@ |
|
|
|
card.style.transform = 'translateY(0)'; |
|
|
|
}); |
|
|
|
|
|
|
|
suppliesGrid.appendChild(card); |
|
|
|
// 添加到DocumentFragment |
|
|
|
fragment.appendChild(card); |
|
|
|
}); |
|
|
|
|
|
|
|
// 一次性替换旧内容,减少页面重排 |
|
|
|
suppliesGrid.innerHTML = ''; |
|
|
|
suppliesGrid.appendChild(fragment); |
|
|
|
|
|
|
|
// 恢复容器高度和滚动位置 |
|
|
|
suppliesGrid.style.height = ''; |
|
|
|
suppliesGrid.style.overflow = ''; |
|
|
|
window.scrollTo(0, scrollPosition); |
|
|
|
} |
|
|
|
</script> |
|
|
|
</body> |
|
|
|
|