Skip to content

Commit e89311d

Browse files
authored
feat: Implement dashboard caching and refresh functionality (#11100)
* feat: Implement dashboard caching and refresh functionality * feat: Enhance dashboard and launcher with hover refresh functionality and improve caching strategy * feat: Introduce dashboard caching utility functions for improved data management
1 parent b5e56c6 commit e89311d

File tree

4 files changed

+249
-19
lines changed

4 files changed

+249
-19
lines changed

frontend/src/layout/components/Sidebar/components/Collapse.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@ const logout = () => {
238238
})
239239
.then(async () => {
240240
await logOutApi();
241+
sessionStorage.removeItem('dashboardCache');
242+
localStorage.removeItem('dashboardCache');
243+
sessionStorage.removeItem('upgradeChecked');
244+
localStorage.removeItem('upgradeChecked');
241245
router.push({ name: 'entrance', params: { code: globalStore.entrance } });
242246
globalStore.setLogStatus(false);
243247
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const DASHBOARD_CACHE_KEY = 'dashboardCache';
2+
3+
type CacheEntry = {
4+
value: any;
5+
expireAt: number;
6+
};
7+
8+
const readCache = (): Record<string, CacheEntry> | null => {
9+
try {
10+
const cacheRaw = localStorage.getItem(DASHBOARD_CACHE_KEY);
11+
return cacheRaw ? JSON.parse(cacheRaw) : {};
12+
} catch {
13+
return null;
14+
}
15+
};
16+
17+
export const getDashboardCache = (key: string) => {
18+
const cache = readCache();
19+
if (!cache) return null;
20+
const entry = cache[key];
21+
if (entry && entry.expireAt > Date.now()) {
22+
return entry.value;
23+
}
24+
return null;
25+
};
26+
27+
export const setDashboardCache = (key: string, value: any, ttl: number) => {
28+
try {
29+
const cacheRaw = localStorage.getItem(DASHBOARD_CACHE_KEY);
30+
const cache = cacheRaw ? JSON.parse(cacheRaw) : {};
31+
cache[key] = {
32+
value,
33+
expireAt: Date.now() + ttl,
34+
};
35+
localStorage.setItem(DASHBOARD_CACHE_KEY, JSON.stringify(cache));
36+
} catch {
37+
localStorage.removeItem(DASHBOARD_CACHE_KEY);
38+
}
39+
};
40+
41+
export const clearDashboardCache = () => {
42+
localStorage.removeItem(DASHBOARD_CACHE_KEY);
43+
};
44+
45+
export const clearDashboardCacheByPrefix = (prefixes: string[]) => {
46+
try {
47+
const cacheRaw = localStorage.getItem(DASHBOARD_CACHE_KEY);
48+
if (!cacheRaw) return;
49+
const cache = JSON.parse(cacheRaw);
50+
Object.keys(cache).forEach((key: string) => {
51+
if (prefixes.some((prefix) => key.startsWith(prefix))) {
52+
delete cache[key];
53+
}
54+
});
55+
localStorage.setItem(DASHBOARD_CACHE_KEY, JSON.stringify(cache));
56+
} catch {
57+
clearDashboardCache();
58+
}
59+
};
60+
61+
export { DASHBOARD_CACHE_KEY };

frontend/src/views/home/app/index.vue

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
<template>
22
<div>
3-
<CardWithHeader :header="$t('app.app')" class="card-interval" v-loading="loading">
3+
<CardWithHeader
4+
:header="$t('app.app')"
5+
class="card-interval"
6+
v-loading="loading"
7+
@mouseenter="refreshLauncherOnHover"
8+
>
49
<template #header-r>
10+
<el-button class="h-button-setting" link icon="Refresh" @click="refreshLauncher" />
511
<el-popover placement="left" :width="226" trigger="click">
612
<el-input size="small" v-model="filter" clearable @input="loadOption()" />
713
<el-table :show-header="false" :data="options" max-height="150px">
@@ -185,25 +191,41 @@ import { changeLauncherStatus, loadAppLauncher, loadAppLauncherOption } from '@/
185191
import i18n from '@/lang';
186192
import { GlobalStore } from '@/store';
187193
import { MsgSuccess } from '@/utils/message';
188-
import { ref } from 'vue';
194+
import { ref, computed } from 'vue';
189195
import { useRouter } from 'vue-router';
190196
import { jumpToPath } from '@/utils/util';
191197
import { jumpToInstall } from '@/utils/app';
192198
import { routerToFileWithPath, routerToNameWithQuery } from '@/utils/router';
199+
import { clearDashboardCacheByPrefix, getDashboardCache, setDashboardCache } from '@/utils/dashboardCache';
193200
194201
const router = useRouter();
195202
const globalStore = GlobalStore();
196203
204+
const DASHBOARD_CACHE_TTL = {
205+
launcherOption: 5 * 60 * 1000,
206+
launcher: 10 * 60 * 1000,
207+
systemIP: 10 * 60 * 1000,
208+
};
209+
210+
const clearLauncherCache = () => {
211+
clearDashboardCacheByPrefix(['appLauncherOption-', 'appLauncher', 'systemIP']);
212+
};
213+
197214
let loading = ref(false);
198215
let apps = ref([]);
199216
const options = ref([]);
200217
const filter = ref();
218+
const launcherFromCache = ref(false);
219+
const launcherOptionFromCache = ref(false);
220+
const systemIPFromCache = ref(false);
221+
const hasRefreshedLauncherOnHover = ref(false);
201222
const mobile = computed(() => {
202223
return globalStore.isMobile();
203224
});
204225
const defaultLink = ref('');
205226
206227
const acceptParams = (): void => {
228+
hasRefreshedLauncherOnHover.value = false;
207229
search();
208230
loadOption();
209231
getConfig();
@@ -215,17 +237,31 @@ const goInstall = (key: string, type: string) => {
215237
}
216238
};
217239
218-
const search = async () => {
240+
const search = async (force?: boolean) => {
219241
loading.value = true;
242+
const cache = force ? null : getDashboardCache('appLauncher');
243+
if (cache !== null) {
244+
apps.value = cache;
245+
launcherFromCache.value = true;
246+
for (const item of apps.value) {
247+
if (item.detail && item.detail.length !== 0) {
248+
item.currentRow = item.detail[0];
249+
}
250+
}
251+
loading.value = false;
252+
return;
253+
}
220254
await loadAppLauncher()
221255
.then((res) => {
222256
loading.value = false;
223257
apps.value = res.data;
258+
launcherFromCache.value = false;
224259
for (const item of apps.value) {
225260
if (item.detail && item.detail.length !== 0) {
226261
item.currentRow = item.detail[0];
227262
}
228263
}
264+
setDashboardCache('appLauncher', apps.value, DASHBOARD_CACHE_TTL.launcher);
229265
})
230266
.finally(() => {
231267
loading.value = false;
@@ -238,7 +274,9 @@ const onChangeStatus = async (row: any) => {
238274
.then(() => {
239275
loading.value = false;
240276
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
277+
clearLauncherCache();
241278
search();
279+
loadOption();
242280
})
243281
.catch(() => {
244282
loading.value = false;
@@ -249,12 +287,18 @@ const toLink = (link: string) => {
249287
window.open(link, '_blank');
250288
};
251289
252-
const getConfig = async () => {
290+
const getConfig = async (force?: boolean) => {
253291
try {
254-
const res = await getAgentSettingByKey('SystemIP');
255-
if (res.data != '') {
256-
defaultLink.value = res.data;
292+
const cache = force ? null : getDashboardCache('systemIP');
293+
if (cache !== null) {
294+
defaultLink.value = cache;
295+
systemIPFromCache.value = true;
296+
return;
257297
}
298+
const res = await getAgentSettingByKey('SystemIP');
299+
defaultLink.value = res.data || '';
300+
systemIPFromCache.value = false;
301+
setDashboardCache('systemIP', defaultLink.value, DASHBOARD_CACHE_TTL.systemIP);
258302
} catch (error) {}
259303
};
260304
@@ -286,9 +330,33 @@ const onOperate = async (operation: string, row: any) => {
286330
});
287331
};
288332
289-
const loadOption = async () => {
333+
const loadOption = async (force?: boolean) => {
334+
const cacheKey = `appLauncherOption-${filter.value || ''}`;
335+
const cache = force ? null : getDashboardCache(cacheKey);
336+
if (cache !== null) {
337+
options.value = cache;
338+
launcherOptionFromCache.value = true;
339+
return;
340+
}
290341
const res = await loadAppLauncherOption(filter.value || '');
291342
options.value = res.data || [];
343+
launcherOptionFromCache.value = false;
344+
setDashboardCache(cacheKey, options.value, DASHBOARD_CACHE_TTL.launcherOption);
345+
};
346+
347+
const refreshLauncher = async () => {
348+
clearLauncherCache();
349+
hasRefreshedLauncherOnHover.value = false;
350+
await Promise.allSettled([loadOption(true), search(true), getConfig(true)]);
351+
};
352+
353+
const refreshLauncherOnHover = async () => {
354+
if (hasRefreshedLauncherOnHover.value) return;
355+
if (!launcherFromCache.value && !launcherOptionFromCache.value && !systemIPFromCache.value) return;
356+
hasRefreshedLauncherOnHover.value = true;
357+
await loadOption(true);
358+
await search(true);
359+
await getConfig(true);
292360
};
293361
294362
defineExpose({

0 commit comments

Comments
 (0)