|
|
|
@ -1671,9 +1671,6 @@ |
|
|
|
</div> |
|
|
|
<!-- 右侧:联系客服、刷新和用户信息 --> |
|
|
|
<div class="title-bar-right"> |
|
|
|
<button onclick="refreshSupplies()" style="background-color: rgba(255, 255, 255, 0.2); color: white; border: 1px solid rgba(255, 255, 255, 0.3); padding: 6px 12px; border-radius: 15px; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; backdrop-filter: blur(10px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); flex-shrink: 0;"> |
|
|
|
刷新数据 |
|
|
|
</button> |
|
|
|
<button onclick="contactCustomerService()" style="background-color: rgba(255, 255, 255, 0.2); color: white; border: 1px solid rgba(255, 255, 255, 0.3); padding: 6px 12px; border-radius: 15px; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; backdrop-filter: blur(10px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); flex-shrink: 0;"> |
|
|
|
联系客服 |
|
|
|
</button> |
|
|
|
@ -2696,16 +2693,61 @@ |
|
|
|
constructor() { |
|
|
|
this.cache = new Map(); |
|
|
|
this.defaultTTL = 5 * 60 * 1000; // 默认缓存5分钟 |
|
|
|
this.maxLocalStorageSize = 4 * 1024 * 1024; // 本地存储最大4MB |
|
|
|
this.cacheVersion = 'v2'; // 缓存版本号 |
|
|
|
this.initialize(); |
|
|
|
} |
|
|
|
|
|
|
|
// 初始化 |
|
|
|
initialize() { |
|
|
|
this.migrateCache(); |
|
|
|
} |
|
|
|
|
|
|
|
// 缓存迁移 |
|
|
|
migrateCache() { |
|
|
|
const oldVersion = localStorage.getItem('cache_version'); |
|
|
|
if (oldVersion !== this.cacheVersion) { |
|
|
|
// 清理旧版本缓存 |
|
|
|
for (let key in localStorage) { |
|
|
|
if (key.startsWith('cache_') && !key.startsWith(`cache_${this.cacheVersion}_`)) { |
|
|
|
localStorage.removeItem(key); |
|
|
|
} |
|
|
|
} |
|
|
|
localStorage.setItem('cache_version', this.cacheVersion); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取带版本的缓存键 |
|
|
|
getVersionedKey(key) { |
|
|
|
return `${this.cacheVersion}_${key}`; |
|
|
|
} |
|
|
|
|
|
|
|
// 设置缓存 |
|
|
|
set(key, value, ttl = this.defaultTTL) { |
|
|
|
const versionedKey = this.getVersionedKey(key); |
|
|
|
const item = { |
|
|
|
value, |
|
|
|
expiry: Date.now() + ttl |
|
|
|
expiry: Date.now() + ttl, |
|
|
|
_timestamp: Date.now() // 添加时间戳用于排序 |
|
|
|
}; |
|
|
|
|
|
|
|
// 更新内存缓存 |
|
|
|
this.cache.set(key, item); |
|
|
|
localStorage.setItem(`cache_${key}`, JSON.stringify(item)); |
|
|
|
|
|
|
|
// 更新本地存储缓存 |
|
|
|
try { |
|
|
|
this.ensureStorageSpace(); |
|
|
|
localStorage.setItem(`cache_${versionedKey}`, JSON.stringify(item)); |
|
|
|
} catch (error) { |
|
|
|
console.error('存储缓存数据失败:', error); |
|
|
|
// 存储失败时清理部分缓存 |
|
|
|
this.clearOldCache(); |
|
|
|
try { |
|
|
|
localStorage.setItem(`cache_${versionedKey}`, JSON.stringify(item)); |
|
|
|
} catch (e) { |
|
|
|
console.error('清理缓存后仍无法存储:', e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取缓存 |
|
|
|
@ -2715,13 +2757,15 @@ |
|
|
|
|
|
|
|
// 如果内存缓存不存在,从localStorage获取 |
|
|
|
if (!item) { |
|
|
|
const stored = localStorage.getItem(`cache_${key}`); |
|
|
|
const versionedKey = this.getVersionedKey(key); |
|
|
|
const stored = localStorage.getItem(`cache_${versionedKey}`); |
|
|
|
if (stored) { |
|
|
|
try { |
|
|
|
item = JSON.parse(stored); |
|
|
|
this.cache.set(key, item); |
|
|
|
} catch (e) { |
|
|
|
console.error('解析缓存失败:', e); |
|
|
|
localStorage.removeItem(`cache_${versionedKey}`); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -2739,7 +2783,8 @@ |
|
|
|
// 删除缓存 |
|
|
|
delete(key) { |
|
|
|
this.cache.delete(key); |
|
|
|
localStorage.removeItem(`cache_${key}`); |
|
|
|
const versionedKey = this.getVersionedKey(key); |
|
|
|
localStorage.removeItem(`cache_${versionedKey}`); |
|
|
|
} |
|
|
|
|
|
|
|
// 清除所有缓存 |
|
|
|
@ -2760,12 +2805,64 @@ |
|
|
|
this.cache.delete(key); |
|
|
|
} |
|
|
|
} |
|
|
|
const versionedPrefix = this.getVersionedKey(prefix); |
|
|
|
for (let key in localStorage) { |
|
|
|
if (key.startsWith(`cache_${prefix}`)) { |
|
|
|
if (key.startsWith(`cache_${versionedPrefix}`)) { |
|
|
|
localStorage.removeItem(key); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 确保存储空间 |
|
|
|
ensureStorageSpace() { |
|
|
|
if (this.getLocalStorageSize() > this.maxLocalStorageSize) { |
|
|
|
this.clearOldCache(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取本地存储大小 |
|
|
|
getLocalStorageSize() { |
|
|
|
let size = 0; |
|
|
|
for (let key in localStorage) { |
|
|
|
if (localStorage.hasOwnProperty(key)) { |
|
|
|
size += localStorage[key].length; |
|
|
|
} |
|
|
|
} |
|
|
|
return size; |
|
|
|
} |
|
|
|
|
|
|
|
// 清理旧缓存 |
|
|
|
clearOldCache() { |
|
|
|
const cacheKeys = []; |
|
|
|
|
|
|
|
// 收集所有缓存键和时间戳 |
|
|
|
for (let key in localStorage) { |
|
|
|
if (key.startsWith('cache_')) { |
|
|
|
try { |
|
|
|
const stored = localStorage.getItem(key); |
|
|
|
const item = JSON.parse(stored); |
|
|
|
cacheKeys.push({ |
|
|
|
key, |
|
|
|
timestamp: item._timestamp || 0 |
|
|
|
}); |
|
|
|
} catch (error) { |
|
|
|
// 解析失败的缓存直接删除 |
|
|
|
localStorage.removeItem(key); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 按时间戳排序,删除最旧的缓存 |
|
|
|
cacheKeys.sort((a, b) => a.timestamp - b.timestamp); |
|
|
|
|
|
|
|
// 删除一半最旧的缓存 |
|
|
|
const deleteCount = Math.ceil(cacheKeys.length / 2); |
|
|
|
for (let i = 0; i < deleteCount; i++) { |
|
|
|
if (cacheKeys[i]) { |
|
|
|
localStorage.removeItem(cacheKeys[i].key); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 实例化缓存管理器 |
|
|
|
@ -2982,15 +3079,23 @@ |
|
|
|
let ws = null; |
|
|
|
let wsReconnectAttempts = 0; |
|
|
|
const maxReconnectAttempts = 5; |
|
|
|
const reconnectDelay = 3000; // 3秒 |
|
|
|
const initialReconnectDelay = 1000; // 初始重连延迟1秒 |
|
|
|
const maxReconnectDelay = 30000; // 最大重连延迟30秒 |
|
|
|
|
|
|
|
// 保存定时器引用,方便后续清理 |
|
|
|
let timers = { |
|
|
|
loadSupplies: null, |
|
|
|
updateCountdowns: null, |
|
|
|
checkAutoOffline: null |
|
|
|
checkAutoOffline: null, |
|
|
|
heartbeat: null, |
|
|
|
heartbeatTimeout: null |
|
|
|
}; |
|
|
|
|
|
|
|
// WebSocket心跳配置 |
|
|
|
const heartbeatInterval = 30000; // 心跳间隔30秒 |
|
|
|
const heartbeatTimeout = 15000; // 心跳超时时间15秒 |
|
|
|
let lastHeartbeatTime = 0; // 最后一次心跳时间 |
|
|
|
|
|
|
|
// 防止loadSupplies并发执行的标志 |
|
|
|
let isLoadingSupplies = false; |
|
|
|
|
|
|
|
@ -3113,6 +3218,8 @@ |
|
|
|
userId: userInfo.userId || userInfo.id |
|
|
|
})); |
|
|
|
} |
|
|
|
// 启动心跳机制 |
|
|
|
startHeartbeat(); |
|
|
|
}; |
|
|
|
|
|
|
|
// 消息接收事件 |
|
|
|
@ -3128,6 +3235,8 @@ |
|
|
|
// 连接关闭事件 |
|
|
|
ws.onclose = function(event) { |
|
|
|
console.log('WebSocket连接已关闭:', event.code, event.reason); |
|
|
|
// 停止心跳机制 |
|
|
|
stopHeartbeat(); |
|
|
|
// 只有在非正常关闭时才重连 |
|
|
|
// 正常关闭码1000表示主动关闭,不需要重连 |
|
|
|
if (event.code !== 1000) { |
|
|
|
@ -3151,13 +3260,126 @@ |
|
|
|
function attemptReconnect() { |
|
|
|
if (wsReconnectAttempts < maxReconnectAttempts) { |
|
|
|
wsReconnectAttempts++; |
|
|
|
console.log(`尝试重新连接WebSocket (${wsReconnectAttempts}/${maxReconnectAttempts})...`); |
|
|
|
setTimeout(initWebSocket, reconnectDelay); |
|
|
|
// 计算指数退避延迟:初始延迟 * 2^(重连次数-1),但不超过最大延迟 |
|
|
|
const delay = Math.min(initialReconnectDelay * Math.pow(2, wsReconnectAttempts - 1), maxReconnectDelay); |
|
|
|
console.log(`尝试重新连接WebSocket (${wsReconnectAttempts}/${maxReconnectAttempts}),延迟 ${delay}ms...`); |
|
|
|
setTimeout(initWebSocket, delay); |
|
|
|
} else { |
|
|
|
console.error('WebSocket重连失败,已达到最大重试次数'); |
|
|
|
// 30秒后重置重连计数,允许再次尝试 |
|
|
|
setTimeout(() => { |
|
|
|
wsReconnectAttempts = 0; |
|
|
|
console.log('WebSocket重连计数已重置,可再次尝试连接'); |
|
|
|
}, 30000); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取当前用户的基础缓存键 |
|
|
|
function getBaseCacheKey() { |
|
|
|
const userInfo = checkLogin(); |
|
|
|
if (!userInfo) return null; |
|
|
|
return `supplies_${userInfo.projectName === '管理员' ? 'admin' : userInfo.userId}`; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取完整货源列表的缓存键 |
|
|
|
function getSuppliesListCacheKey() { |
|
|
|
const baseKey = getBaseCacheKey(); |
|
|
|
return baseKey ? `${baseKey}_list` : null; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取单个货源的缓存键 |
|
|
|
function getSupplyCacheKey(supplyId) { |
|
|
|
const baseKey = getBaseCacheKey(); |
|
|
|
return baseKey ? `${baseKey}_supply_${supplyId}` : null; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取特定状态货源的缓存键 |
|
|
|
function getSuppliesByStatusCacheKey(status) { |
|
|
|
const baseKey = getBaseCacheKey(); |
|
|
|
return baseKey ? `${baseKey}_status_${status}` : null; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取搜索结果的缓存键 |
|
|
|
function getSearchCacheKey(keyword) { |
|
|
|
const baseKey = getBaseCacheKey(); |
|
|
|
return baseKey ? `${baseKey}_search_${encodeURIComponent(keyword)}` : null; |
|
|
|
} |
|
|
|
|
|
|
|
// 更新缓存中的单个货源数据 |
|
|
|
function updateSupplyInCache(supplyId, updatedData) { |
|
|
|
try { |
|
|
|
// 获取完整列表缓存键 |
|
|
|
const listCacheKey = getSuppliesListCacheKey(); |
|
|
|
if (!listCacheKey) { |
|
|
|
console.error('无法获取缓存键,用户未登录'); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// 获取单个货源缓存键 |
|
|
|
const supplyCacheKey = getSupplyCacheKey(supplyId); |
|
|
|
|
|
|
|
// 1. 更新完整列表缓存 |
|
|
|
const cachedSupplies = cacheManager.get(listCacheKey); |
|
|
|
|
|
|
|
if (cachedSupplies && Array.isArray(cachedSupplies)) { |
|
|
|
const supplyIndex = cachedSupplies.findIndex(supply => String(supply.id) === String(supplyId)); |
|
|
|
|
|
|
|
if (supplyIndex !== -1) { |
|
|
|
// 创建更新后的货源数据 |
|
|
|
const updatedSupply = { ...cachedSupplies[supplyIndex], ...updatedData }; |
|
|
|
// 创建新的货源数组,避免直接修改原数组 |
|
|
|
const updatedSupplies = [...cachedSupplies]; |
|
|
|
updatedSupplies[supplyIndex] = updatedSupply; |
|
|
|
|
|
|
|
// 更新完整列表缓存 |
|
|
|
cacheManager.set(listCacheKey, updatedSupplies, 60 * 60 * 1000); |
|
|
|
|
|
|
|
// 2. 更新单个货源缓存 |
|
|
|
if (supplyCacheKey) { |
|
|
|
cacheManager.set(supplyCacheKey, updatedSupply, 60 * 60 * 1000); |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 如果状态发生变化,清除状态相关的缓存 |
|
|
|
if (updatedData.status) { |
|
|
|
const oldStatus = cachedSupplies[supplyIndex].status; |
|
|
|
if (oldStatus !== updatedData.status) { |
|
|
|
// 清除旧状态和新状态的缓存 |
|
|
|
const oldStatusCacheKey = getSuppliesByStatusCacheKey(oldStatus); |
|
|
|
const newStatusCacheKey = getSuppliesByStatusCacheKey(updatedData.status); |
|
|
|
if (oldStatusCacheKey) cacheManager.delete(oldStatusCacheKey); |
|
|
|
if (newStatusCacheKey) cacheManager.delete(newStatusCacheKey); |
|
|
|
// 清除搜索结果缓存,因为状态变化可能影响搜索 |
|
|
|
clearSearchCache(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
console.log('缓存中的货源数据已更新:', supplyId, '更新字段:', Object.keys(updatedData)); |
|
|
|
return updatedSupplies; |
|
|
|
} else { |
|
|
|
console.warn('在缓存中未找到货源:', supplyId); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.warn('缓存中无货源数据或数据格式不正确'); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('更新缓存中的货源数据失败:', error); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 清除搜索结果缓存 |
|
|
|
function clearSearchCache() { |
|
|
|
const baseKey = getBaseCacheKey(); |
|
|
|
if (!baseKey) return; |
|
|
|
|
|
|
|
// 清除所有搜索相关的缓存 |
|
|
|
const searchPrefix = `${baseKey}_search_`; |
|
|
|
cacheManager.clearByPrefix(searchPrefix); |
|
|
|
console.log('搜索结果缓存已清除'); |
|
|
|
} |
|
|
|
|
|
|
|
// 处理WebSocket消息 |
|
|
|
function handleWebSocketMessage(message) { |
|
|
|
try { |
|
|
|
@ -3167,24 +3389,101 @@ |
|
|
|
|
|
|
|
switch (data.type) { |
|
|
|
case 'supply_update': |
|
|
|
// 货源更新通知,重新加载数据 |
|
|
|
// 货源更新通知 |
|
|
|
if (data.supplyId && data.updatedData) { |
|
|
|
// 增量更新缓存 |
|
|
|
console.log('收到货源更新通知,执行增量更新:', data.supplyId); |
|
|
|
try { |
|
|
|
const updatedSupplies = updateSupplyInCache(data.supplyId, data.updatedData); |
|
|
|
if (updatedSupplies) { |
|
|
|
// 更新内存中的数据 |
|
|
|
supplyData.supplies = updatedSupplies; |
|
|
|
processSupplyData(updatedSupplies); |
|
|
|
renderSupplyLists(); |
|
|
|
} else { |
|
|
|
// 缓存更新失败,重新加载数据 |
|
|
|
console.warn('缓存更新失败,执行全量加载'); |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('处理货源更新通知失败:', error); |
|
|
|
// 发生错误时,重新加载数据 |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 没有具体的更新数据,重新加载 |
|
|
|
console.log('收到货源更新通知,重新加载数据'); |
|
|
|
loadSupplies(true); // 强制刷新,跳过缓存 |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 'supply_lock': |
|
|
|
// 货源锁定状态更新 |
|
|
|
if (data.supplyId && data.locked !== undefined) { |
|
|
|
console.log('收到货源锁定状态更新,执行增量更新:', data.supplyId, '锁定状态:', data.locked); |
|
|
|
try { |
|
|
|
const updatedSupplies = updateSupplyInCache(data.supplyId, { locked: data.locked }); |
|
|
|
if (updatedSupplies) { |
|
|
|
supplyData.supplies = updatedSupplies; |
|
|
|
processSupplyData(updatedSupplies); |
|
|
|
renderSupplyLists(); |
|
|
|
} else { |
|
|
|
console.warn('缓存更新失败,执行全量加载'); |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('处理货源锁定状态更新失败:', error); |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.log('收到货源锁定状态更新,重新加载数据'); |
|
|
|
loadSupplies(true); // 强制刷新,跳过缓存 |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 'supply_status_change': |
|
|
|
// 货源状态变更 |
|
|
|
if (data.supplyId && data.status) { |
|
|
|
console.log('收到货源状态变更,执行增量更新:', data.supplyId, '新状态:', data.status); |
|
|
|
try { |
|
|
|
const updatedSupplies = updateSupplyInCache(data.supplyId, { status: data.status }); |
|
|
|
if (updatedSupplies) { |
|
|
|
supplyData.supplies = updatedSupplies; |
|
|
|
processSupplyData(updatedSupplies); |
|
|
|
renderSupplyLists(); |
|
|
|
} else { |
|
|
|
console.warn('缓存更新失败,执行全量加载'); |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('处理货源状态变更失败:', error); |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.log('收到货源状态变更,重新加载数据'); |
|
|
|
loadSupplies(true); // 强制刷新,跳过缓存 |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 'auto_offline': |
|
|
|
// 自动下架通知 |
|
|
|
if (data.supplyId) { |
|
|
|
console.log('收到自动下架通知,执行增量更新:', data.supplyId); |
|
|
|
try { |
|
|
|
const updatedSupplies = updateSupplyInCache(data.supplyId, { status: 'draft' }); |
|
|
|
if (updatedSupplies) { |
|
|
|
supplyData.supplies = updatedSupplies; |
|
|
|
processSupplyData(updatedSupplies); |
|
|
|
renderSupplyLists(); |
|
|
|
} else { |
|
|
|
console.warn('缓存更新失败,执行全量加载'); |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('处理自动下架通知失败:', error); |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.log('收到自动下架通知,重新加载数据'); |
|
|
|
loadSupplies(true); // 强制刷新,跳过缓存 |
|
|
|
loadSupplies(true); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 'ping': |
|
|
|
// 心跳响应 |
|
|
|
@ -3192,6 +3491,15 @@ |
|
|
|
ws.send(JSON.stringify({ type: 'pong' })); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 'pong': |
|
|
|
// 收到心跳响应 |
|
|
|
console.log('收到WebSocket心跳响应'); |
|
|
|
// 清除心跳超时定时器 |
|
|
|
if (timers.heartbeatTimeout) { |
|
|
|
clearTimeout(timers.heartbeatTimeout); |
|
|
|
timers.heartbeatTimeout = null; |
|
|
|
} |
|
|
|
break; |
|
|
|
default: |
|
|
|
console.log('未知的WebSocket消息类型:', data.type); |
|
|
|
} |
|
|
|
@ -3209,6 +3517,53 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 发送心跳 |
|
|
|
function sendHeartbeat() { |
|
|
|
if (ws && ws.readyState === WebSocket.OPEN) { |
|
|
|
console.log('发送WebSocket心跳'); |
|
|
|
sendWebSocketMessage({ type: 'ping' }); |
|
|
|
lastHeartbeatTime = Date.now(); |
|
|
|
|
|
|
|
// 设置心跳超时定时器 |
|
|
|
if (timers.heartbeatTimeout) { |
|
|
|
clearTimeout(timers.heartbeatTimeout); |
|
|
|
} |
|
|
|
timers.heartbeatTimeout = setTimeout(() => { |
|
|
|
console.error('WebSocket心跳超时,连接可能已断开'); |
|
|
|
// 心跳超时,主动关闭连接并尝试重连 |
|
|
|
if (ws) { |
|
|
|
ws.close(1006, 'Heartbeat timeout'); |
|
|
|
} |
|
|
|
}, heartbeatTimeout); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 启动心跳 |
|
|
|
function startHeartbeat() { |
|
|
|
// 停止已有的心跳 |
|
|
|
stopHeartbeat(); |
|
|
|
|
|
|
|
// 立即发送一次心跳 |
|
|
|
sendHeartbeat(); |
|
|
|
|
|
|
|
// 设置心跳间隔 |
|
|
|
timers.heartbeat = setInterval(sendHeartbeat, heartbeatInterval); |
|
|
|
console.log('WebSocket心跳机制已启动,间隔', heartbeatInterval, 'ms'); |
|
|
|
} |
|
|
|
|
|
|
|
// 停止心跳 |
|
|
|
function stopHeartbeat() { |
|
|
|
if (timers.heartbeat) { |
|
|
|
clearInterval(timers.heartbeat); |
|
|
|
timers.heartbeat = null; |
|
|
|
} |
|
|
|
if (timers.heartbeatTimeout) { |
|
|
|
clearTimeout(timers.heartbeatTimeout); |
|
|
|
timers.heartbeatTimeout = null; |
|
|
|
} |
|
|
|
console.log('WebSocket心跳机制已停止'); |
|
|
|
} |
|
|
|
|
|
|
|
// 移除复杂的触摸事件处理,使用浏览器默认滚动行为 |
|
|
|
function preventIOSDrag() { |
|
|
|
// 不再阻止任何默认行为,让浏览器处理滚动 |
|
|
|
@ -5205,6 +5560,16 @@ |
|
|
|
|
|
|
|
try { |
|
|
|
console.log('开始加载联系人数据...'); |
|
|
|
|
|
|
|
// 尝试从缓存加载 |
|
|
|
const cachedContacts = cacheManager.get('contacts'); |
|
|
|
if (cachedContacts) { |
|
|
|
contacts = cachedContacts; |
|
|
|
console.log('从缓存加载联系人数据,共', contacts.length, '个联系人'); |
|
|
|
updateContactSelects(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const response = await fetch('/api/contacts'); |
|
|
|
if (!response.ok) { |
|
|
|
throw new Error(`服务器响应异常: ${response.status} ${response.statusText}`); |
|
|
|
@ -5215,36 +5580,20 @@ |
|
|
|
contacts = result.data || []; |
|
|
|
console.log('联系人数据加载成功,共', contacts.length, '个联系人:', contacts); |
|
|
|
|
|
|
|
// 保存到本地缓存,添加时间戳和版本号 |
|
|
|
const contactsCache = { |
|
|
|
data: contacts, |
|
|
|
version: '1.0', |
|
|
|
timestamp: Date.now() |
|
|
|
}; |
|
|
|
localStorage.setItem('contactsCache', JSON.stringify(contactsCache)); |
|
|
|
// 保存到缓存,设置7天过期时间 |
|
|
|
cacheManager.set('contacts', contacts, 7 * 24 * 60 * 60 * 1000); |
|
|
|
|
|
|
|
updateContactSelects(); |
|
|
|
} catch (error) { |
|
|
|
console.error('加载联系人数据失败:', error); |
|
|
|
|
|
|
|
// 尝试从本地缓存加载 |
|
|
|
try { |
|
|
|
const cachedContacts = localStorage.getItem('contactsCache'); |
|
|
|
// 尝试从缓存加载 |
|
|
|
const cachedContacts = cacheManager.get('contacts'); |
|
|
|
if (cachedContacts) { |
|
|
|
const contactsCache = JSON.parse(cachedContacts); |
|
|
|
// 检查缓存是否有效(7天内) |
|
|
|
const cacheExpiry = 7 * 24 * 60 * 60 * 1000; |
|
|
|
if (Date.now() - contactsCache.timestamp < cacheExpiry) { |
|
|
|
contacts = contactsCache.data || []; |
|
|
|
console.log('从本地缓存加载联系人数据,共', contacts.length, '个联系人:', contacts); |
|
|
|
contacts = cachedContacts; |
|
|
|
console.log('API失败,从缓存加载联系人数据,共', contacts.length, '个联系人'); |
|
|
|
updateContactSelects(); |
|
|
|
return; |
|
|
|
} else { |
|
|
|
console.log('联系人缓存已过期'); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (cacheError) { |
|
|
|
console.error('加载联系人缓存失败:', cacheError); |
|
|
|
} |
|
|
|
|
|
|
|
// 出错且无有效缓存时,将contacts设为空数组 |
|
|
|
@ -5303,7 +5652,8 @@ |
|
|
|
if (!userInfo) return; |
|
|
|
|
|
|
|
// 生成缓存键 |
|
|
|
const cacheKey = `supplies_cache_${userInfo.projectName === '管理员' ? 'admin' : userInfo.userId}`; |
|
|
|
const cacheKey = getSuppliesListCacheKey(); |
|
|
|
if (!cacheKey) return; |
|
|
|
|
|
|
|
// 重置所有状态的分页数据 |
|
|
|
supplyData.pagination = { |
|
|
|
@ -5321,20 +5671,14 @@ |
|
|
|
draft: [] |
|
|
|
}; |
|
|
|
|
|
|
|
// 尝试从本地缓存获取数据 |
|
|
|
// 尝试从缓存获取数据 |
|
|
|
if (!forceRefresh) { |
|
|
|
const cachedData = localStorage.getItem(cacheKey); |
|
|
|
const cachedData = cacheManager.get(cacheKey); |
|
|
|
if (cachedData) { |
|
|
|
try { |
|
|
|
const supplies = JSON.parse(cachedData); |
|
|
|
console.log('从本地缓存加载货源数据:', supplies.length); |
|
|
|
processSupplyData(supplies); |
|
|
|
console.log('从缓存加载货源数据:', cachedData.length); |
|
|
|
processSupplyData(cachedData); |
|
|
|
renderSupplyLists(); |
|
|
|
return; |
|
|
|
} catch (error) { |
|
|
|
console.error('解析缓存数据失败:', error); |
|
|
|
localStorage.removeItem(cacheKey); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -5357,9 +5701,18 @@ |
|
|
|
if (result.success) { |
|
|
|
console.log('加载到的货源数量:', result.data.list.length); |
|
|
|
|
|
|
|
// 存储到本地缓存 |
|
|
|
localStorage.setItem(cacheKey, JSON.stringify(result.data.list)); |
|
|
|
console.log('货源数据已存储到本地缓存'); |
|
|
|
// 存储到缓存,设置1小时过期时间 |
|
|
|
cacheManager.set(cacheKey, result.data.list, 60 * 60 * 1000); |
|
|
|
|
|
|
|
// 同时为每个货源创建单独的缓存 |
|
|
|
result.data.list.forEach(supply => { |
|
|
|
const supplyCacheKey = getSupplyCacheKey(supply.id); |
|
|
|
if (supplyCacheKey) { |
|
|
|
cacheManager.set(supplyCacheKey, supply, 60 * 60 * 1000); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
console.log('货源数据已存储到缓存'); |
|
|
|
|
|
|
|
processSupplyData(result.data.list); |
|
|
|
renderSupplyLists(); |
|
|
|
@ -5368,20 +5721,13 @@ |
|
|
|
console.error('加载货源失败:', error); |
|
|
|
|
|
|
|
// 尝试从缓存加载(如果有) |
|
|
|
const userInfo = checkLogin(); |
|
|
|
if (userInfo) { |
|
|
|
const cacheKey = `supplies_cache_${userInfo.projectName === '管理员' ? 'admin' : userInfo.userId}`; |
|
|
|
const cachedData = localStorage.getItem(cacheKey); |
|
|
|
const cacheKey = getSuppliesListCacheKey(); |
|
|
|
if (cacheKey) { |
|
|
|
const cachedData = cacheManager.get(cacheKey); |
|
|
|
if (cachedData) { |
|
|
|
try { |
|
|
|
const supplies = JSON.parse(cachedData); |
|
|
|
console.log('API失败,从本地缓存加载货源数据:', supplies.length); |
|
|
|
processSupplyData(supplies); |
|
|
|
console.log('API失败,从缓存加载货源数据:', cachedData.length); |
|
|
|
processSupplyData(cachedData); |
|
|
|
renderSupplyLists(); |
|
|
|
} catch (cacheError) { |
|
|
|
console.error('解析缓存数据失败:', cacheError); |
|
|
|
localStorage.removeItem(cacheKey); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} finally { |
|
|
|
@ -5474,6 +5820,11 @@ |
|
|
|
renderSupplyList('pending', supplyData.pendingSupplies); |
|
|
|
renderSupplyList('rejected', supplyData.rejectedSupplies); |
|
|
|
renderSupplyList('draft', supplyData.draftSupplies); |
|
|
|
|
|
|
|
// 渲染完成后检查并加载可见的懒加载图片 |
|
|
|
setTimeout(() => { |
|
|
|
checkLazyLoadImages(); |
|
|
|
}, 100); |
|
|
|
} |
|
|
|
|
|
|
|
// 渲染单个货源列表 |
|
|
|
|