From e57ee5189dc2ec0609dfd26f1ef1939278a33c92 Mon Sep 17 00:00:00 2001 From: jingrow Date: Sat, 1 Nov 2025 15:09:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96pagetype=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E9=A1=B5Link=E7=B1=BB=E5=9E=8B=E5=AD=97=E6=AE=B5=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=80=BB=E8=BE=91=EF=BC=8C=E5=A2=9E=E5=8A=A0=E9=98=B2?= =?UTF-8?q?=E6=8A=96=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/core/pagetype/GenericListPage.vue | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) 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()