diff --git a/src/composables/useSilentSettingRefresh.ts b/src/composables/useSilentSettingRefresh.ts new file mode 100644 index 00000000..f4a70bb3 --- /dev/null +++ b/src/composables/useSilentSettingRefresh.ts @@ -0,0 +1,33 @@ +import { type MaybeRefOrGetter, toValue } from 'vue' +import { useKeepAliveRefresh, type KeepAliveRefreshContext } from '@/composables/useKeepAliveRefresh' + +type RefreshHandler = (context?: KeepAliveRefreshContext) => void | Promise + +interface SilentSettingRefreshOptions { + active?: MaybeRefOrGetter +} + +function isEditingFormField() { + if (typeof document === 'undefined') return false + + const element = document.activeElement + if (!(element instanceof HTMLElement)) return false + + // 设置页大多是可编辑表单,正在输入时跳过静默刷新,避免覆盖用户未保存内容。 + return Boolean(element.closest('input, textarea, select, [contenteditable="true"], .ace_text-input')) +} + +/** + * 设置面板重新可见时静默刷新数据;如果用户正在编辑表单,则本轮刷新让路给输入体验。 + */ +export function useSilentSettingRefresh(refresh: RefreshHandler, options: SilentSettingRefreshOptions = {}) { + return useKeepAliveRefresh( + async context => { + if (context?.silent && isEditingFormField()) return + await refresh(context) + }, + { + active: options.active === undefined ? undefined : () => Boolean(toValue(options.active)), + }, + ) +} diff --git a/src/layouts/components/ShortcutBar.vue b/src/layouts/components/ShortcutBar.vue index 9e847a57..bbd21ef1 100644 --- a/src/layouts/components/ShortcutBar.vue +++ b/src/layouts/components/ShortcutBar.vue @@ -433,7 +433,7 @@ onMounted(() => { scrollable :fullscreen="!display.mdAndUp.value" > - + @@ -442,7 +442,7 @@ onMounted(() => { - + @@ -492,3 +492,24 @@ onMounted(() => { + + diff --git a/src/pages/setting.vue b/src/pages/setting.vue index 5dae505b..7991542f 100644 --- a/src/pages/setting.vue +++ b/src/pages/setting.vue @@ -19,6 +19,26 @@ const AccountSettingSearch = defineAsyncComponent(() => import('@/views/setting/ const AccountSettingSubscribe = defineAsyncComponent(() => import('@/views/setting/AccountSettingSubscribe.vue')) const AccountSettingNotification = defineAsyncComponent(() => import('@/views/setting/AccountSettingNotification.vue')) +const visitedTabs = ref(new Set()) + +const settingTabComponents = [ + { value: 'system', component: AccountSettingSystem }, + { value: 'directory', component: AccountSettingDirectory }, + { value: 'site', component: AccountSettingSite }, + { value: 'rule', component: AccountSettingRule }, + { value: 'search', component: AccountSettingSearch }, + { value: 'subscribe', component: AccountSettingSubscribe }, + { value: 'notification', component: AccountSettingNotification }, +] + +function markTabVisited(tab: string) { + if (!tab) return + + const nextTabs = new Set(visitedTabs.value) + nextTabs.add(tab) + visitedTabs.value = nextTabs +} + // 使用动态标签页 const { registerHeaderTab } = useDynamicHeaderTab() @@ -34,71 +54,23 @@ onMounted(() => { if (!activeTab.value && settingTabs.value.length > 0) { activeTab.value = settingTabs.value[0].tab } + markTabVisited(activeTab.value) }) + +watch(activeTab, markTabVisited, { immediate: true })