From 943cb1815d99c07f62f115aae350abb904f1ad29 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Sun, 22 Mar 2026 23:12:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8C=81=E4=B9=85=E5=8C=96?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E9=A1=B5=E5=BC=80=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layout/components/Settings/index.vue | 18 ++++++++++ src/layout/components/TagsView/index.vue | 6 +++- src/settings.js | 7 +++- src/store/modules/settings.js | 3 +- src/store/modules/tagsView.js | 45 ++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue index df35699..2353fe4 100644 --- a/src/layout/components/Settings/index.vue +++ b/src/layout/components/Settings/index.vue @@ -63,6 +63,13 @@ +
+ 持久化标签页 + + + +
+
显示页签图标 @@ -120,6 +127,7 @@ const showSettings = ref(false) const navType = ref(settingsStore.navType) const theme = ref(settingsStore.theme) const sideTheme = ref(settingsStore.sideTheme) +const tagsViewPersist = ref(settingsStore.tagsViewPersist) const storeSettings = computed(() => settingsStore) const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]) @@ -128,6 +136,11 @@ function dynamicTitleChange() { useSettingsStore().setTitle(useSettingsStore().title) } +function tagsViewPersistChange(val) { + settingsStore.tagsViewPersist = val + tagsViewPersist.value = val +} + function themeChange(val) { settingsStore.theme = val handleThemeStyle(val) @@ -164,10 +177,14 @@ watch(() => navType, val => { function saveSetting() { proxy.$modal.loading("正在保存到本地,请稍候...") + if (!tagsViewPersist.value) { + proxy.$cache.local.remove('tags-view-visited') + } let layoutSetting = { "navType": storeSettings.value.navType, "tagsView": storeSettings.value.tagsView, "tagsIcon": storeSettings.value.tagsIcon, + "tagsViewPersist": storeSettings.value.tagsViewPersist, "fixedHeader": storeSettings.value.fixedHeader, "sidebarLogo": storeSettings.value.sidebarLogo, "dynamicTitle": storeSettings.value.dynamicTitle, @@ -180,6 +197,7 @@ function saveSetting() { } function resetSetting() { + proxy.$cache.local.remove('tags-view-visited') proxy.$modal.loading("正在清除设置缓存并刷新,请稍候...") localStorage.removeItem("layout-setting") setTimeout("window.location.reload()", 1000) diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue index b621120..37de042 100644 --- a/src/layout/components/TagsView/index.vue +++ b/src/layout/components/TagsView/index.vue @@ -117,6 +117,7 @@ const visitedViews = computed(() => useTagsViewStore().visitedViews) const routes = computed(() => usePermissionStore().routes) const theme = computed(() => useSettingsStore().theme) const tagsIcon = computed(() => useSettingsStore().tagsIcon) +const tagsViewPersist = computed(() => useSettingsStore().tagsViewPersist) // 下拉菜单针对当前激活的 tag const selectedDropdownTag = computed(() => visitedViews.value.find(v => isActive(v)) || {}) @@ -207,11 +208,14 @@ function filterAffixTags(routes, basePath = '') { } function initTags() { + if (tagsViewPersist) { + useTagsViewStore().loadPersistedViews() + } const res = filterAffixTags(routes.value) affixTags.value = res for (const tag of res) { if (tag.name) { - useTagsViewStore().addVisitedView(tag) + useTagsViewStore().addAffixView(tag) } } } diff --git a/src/settings.js b/src/settings.js index ac7402e..22a7bdb 100644 --- a/src/settings.js +++ b/src/settings.js @@ -23,7 +23,12 @@ export default { * 是否显示 tagsView */ tagsView: true, - + + /** + * 持久化标签页 + */ + tagsViewPersist: false, + /** * 显示页签图标 */ diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js index 770d8d4..4cf4563 100644 --- a/src/store/modules/settings.js +++ b/src/store/modules/settings.js @@ -5,7 +5,7 @@ import { useDynamicTitle } from '@/utils/dynamicTitle' const isDark = useDark() const toggleDark = useToggle(isDark) -const { sideTheme, showSettings, navType, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings +const { sideTheme, showSettings, navType, tagsView, tagsViewPersist, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' @@ -19,6 +19,7 @@ const useSettingsStore = defineStore( showSettings: showSettings, navType: storageSetting.navType === undefined ? navType : storageSetting.navType, tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, + tagsViewPersist: storageSetting.tagsViewPersist === undefined ? tagsViewPersist : storageSetting.tagsViewPersist, tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon, fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, diff --git a/src/store/modules/tagsView.js b/src/store/modules/tagsView.js index b0d4ca1..9ea96d9 100644 --- a/src/store/modules/tagsView.js +++ b/src/store/modules/tagsView.js @@ -1,3 +1,26 @@ +import cache from '@/plugins/cache' +import useSettingsStore from '@/store/modules/settings' + +const PERSIST_KEY = 'tags-view-visited' + +function isPersistEnabled() { + return useSettingsStore().tagsViewPersist +} + +function saveVisitedViews(views) { + if (!isPersistEnabled()) return + const toSave = views.filter(v => !(v.meta && v.meta.affix)).map(v => ({ path: v.path, fullPath: v.fullPath, name: v.name, title: v.title, query: v.query, meta: v.meta })) + cache.local.setJSON(PERSIST_KEY, toSave) +} + +function loadVisitedViews() { + return cache.local.getJSON(PERSIST_KEY) || [] +} + +function clearVisitedViews() { + cache.local.remove(PERSIST_KEY) +} + const useTagsViewStore = defineStore( 'tags-view', { @@ -26,6 +49,15 @@ const useTagsViewStore = defineStore( title: view.meta.title || 'no-name' }) ) + saveVisitedViews(this.visitedViews) + }, + addAffixView(view) { + if (this.visitedViews.some(v => v.path === view.path)) return + this.visitedViews.unshift( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) }, addCachedView(view) { if (this.cachedViews.includes(view.name)) return @@ -52,6 +84,7 @@ const useTagsViewStore = defineStore( } } this.iframeViews = this.iframeViews.filter(item => item.path !== view.path) + saveVisitedViews(this.visitedViews) resolve([...this.visitedViews]) }) }, @@ -84,6 +117,7 @@ const useTagsViewStore = defineStore( return v.meta.affix || v.path === view.path }) this.iframeViews = this.iframeViews.filter(item => item.path === view.path) + saveVisitedViews(this.visitedViews) resolve([...this.visitedViews]) }) }, @@ -95,6 +129,7 @@ const useTagsViewStore = defineStore( } else { this.cachedViews = [] } + saveVisitedViews(this.visitedViews) resolve([...this.cachedViews]) }) }, @@ -113,6 +148,7 @@ const useTagsViewStore = defineStore( const affixTags = this.visitedViews.filter(tag => tag.meta.affix) this.visitedViews = affixTags this.iframeViews = [] + clearVisitedViews() resolve([...this.visitedViews]) }) }, @@ -150,6 +186,7 @@ const useTagsViewStore = defineStore( } return false }) + saveVisitedViews(this.visitedViews) resolve([...this.visitedViews]) }) }, @@ -173,8 +210,16 @@ const useTagsViewStore = defineStore( } return false }) + saveVisitedViews(this.visitedViews) resolve([...this.visitedViews]) }) + }, + // 恢复持久化的 tags + loadPersistedViews() { + const views = loadVisitedViews() + views.forEach(view => { + this.addVisitedView(view) + }) } } })