diff --git a/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue b/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue index 2be2c9c..3ace2bf 100644 --- a/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue +++ b/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue @@ -291,6 +291,47 @@ const cardBadges = computed(() => { const metaFields = ref([]) const linkTitleCache = ref>({}) const pageTypeConfigCache = ref>({}) +let saveCacheTimer: ReturnType | null = null + +// 从localStorage恢复缓存(避免刷新时抖动) +function loadCacheFromStorage() { + try { + const cached = localStorage.getItem(`linkTitleCache:${entity.value}`) + if (cached) { + const parsed = JSON.parse(cached) + // 合并到当前缓存,但不覆盖已有值 + Object.keys(parsed).forEach(key => { + if (!linkTitleCache.value[key]) { + linkTitleCache.value[key] = parsed[key] + } + }) + } + } catch (error) { + console.error('加载缓存失败:', error) + } +} + +// 保存缓存到localStorage(带防抖,避免频繁写入) +function saveCacheToStorage() { + if (saveCacheTimer) { + clearTimeout(saveCacheTimer) + } + saveCacheTimer = setTimeout(() => { + try { + // 限制缓存大小,只保存最近1000个条目 + const cacheEntries = Object.entries(linkTitleCache.value) + if (cacheEntries.length > 1000) { + // 只保留最新的1000个 + const recentEntries = cacheEntries.slice(-1000) + linkTitleCache.value = Object.fromEntries(recentEntries) + } + localStorage.setItem(`linkTitleCache:${entity.value}`, JSON.stringify(linkTitleCache.value)) + } catch (error) { + console.error('保存缓存失败:', error) + } + saveCacheTimer = null + }, 1000) // 1秒防抖 +} async function loadMeta() { loading.value = true @@ -301,6 +342,9 @@ async function loadMeta() { // 检查是否为单页模式 isSinglePage.value = await isSinglePageType(entity.value) + + // 加载缓存的Link标题(刷新时减少抖动) + loadCacheFromStorage() } finally { loading.value = false } @@ -410,6 +454,9 @@ async function preloadLinkTitles() { linkTitleCache.value[cacheKey] = name } }) + + // 保存到localStorage(防抖,避免频繁写入) + saveCacheToStorage() } catch (error) { console.error(`批量加载${pagetype}的标题失败:`, error) // 失败时缓存原值 @@ -477,10 +524,8 @@ async function loadData() { total.value = res.data.total } - // 预加载Link字段的title_field值(后台执行,不阻塞UI) - preloadLinkTitles().catch(err => { - console.error('预加载Link标题失败:', err) - }) + // 预加载Link字段的title_field值(在loading期间同步执行,避免抖动) + await preloadLinkTitles() } catch (error) { console.error('Load data error:', error) message.error(t('Load failed')) @@ -510,8 +555,10 @@ watch(() => route.params.entity, async (newEntity, oldEntity) => { searchQuery.value = '' selectedKeys.value = [] filters.value = {} // 重置过滤条件 + // 清空缓存(新entity使用新的缓存) + linkTitleCache.value = {} // 重新加载元数据和数据 - await loadMeta() + await loadMeta() // loadMeta中会加载新entity的缓存 // 只有在非单页模式下才加载列表数据 if (!isSinglePage.value) { await loadData()