diff --git a/README.md b/README.md
index 9b669db4..eff06374 100644
--- a/README.md
+++ b/README.md
@@ -346,13 +346,14 @@ console.log(t('common.confirm'));
- **邮箱**: xlsea@linux.do
- **作者主页**: https://gitee.com/xlsea
+
- **作者**: Elio
- **邮箱**: 1983933789@qq.com
- **作者主页**: https://gitee.com/ahcode
## 💬 交流群
-
+
## 🧧 捐献作者
diff --git a/packages/hooks/src/use-count-down.ts b/packages/hooks/src/use-count-down.ts
index bfad064b..4f95b731 100644
--- a/packages/hooks/src/use-count-down.ts
+++ b/packages/hooks/src/use-count-down.ts
@@ -2,40 +2,59 @@ import { computed, onScopeDispose, ref } from 'vue';
import { useRafFn } from '@vueuse/core';
/**
- * count down
+ * A hook for implementing a countdown timer. It uses `requestAnimationFrame` for smooth and accurate timing,
+ * independent of the screen refresh rate.
*
- * @param seconds - count down seconds
+ * @param initialSeconds - The total number of seconds for the countdown.
*/
-export default function useCountDown(seconds: number) {
- const FPS_PER_SECOND = 60;
+export default function useCountDown(initialSeconds: number) {
+ const remainingSeconds = ref(0);
- const fps = ref(0);
+ const count = computed(() => Math.ceil(remainingSeconds.value));
- const count = computed(() => Math.ceil(fps.value / FPS_PER_SECOND));
-
- const isCounting = computed(() => fps.value > 0);
+ const isCounting = computed(() => remainingSeconds.value > 0);
const { pause, resume } = useRafFn(
- () => {
- if (fps.value > 0) {
- fps.value -= 1;
- } else {
+ ({ delta }) => {
+ // delta: milliseconds elapsed since the last frame.
+
+ // If countdown already reached zero or below, ensure it's 0 and stop.
+ if (remainingSeconds.value <= 0) {
+ remainingSeconds.value = 0;
+ pause();
+ return;
+ }
+
+ // Calculate seconds passed since the last frame.
+ const secondsPassed = delta / 1000;
+ remainingSeconds.value -= secondsPassed;
+
+ // If countdown has finished after decrementing.
+ if (remainingSeconds.value <= 0) {
+ remainingSeconds.value = 0;
pause();
}
},
- { immediate: false }
+ { immediate: false } // The timer does not start automatically.
);
- function start(updateSeconds: number = seconds) {
- fps.value = FPS_PER_SECOND * updateSeconds;
+ /**
+ * Starts the countdown.
+ *
+ * @param [updatedSeconds=initialSeconds] - Optionally, start with a new duration. Default is `initialSeconds`
+ */
+ function start(updatedSeconds: number = initialSeconds) {
+ remainingSeconds.value = updatedSeconds;
resume();
}
+ /** Stops the countdown and resets the remaining time to 0. */
function stop() {
- fps.value = 0;
+ remainingSeconds.value = 0;
pause();
}
+ // Ensure the rAF loop is cleaned up when the component is unmounted.
onScopeDispose(() => {
pause();
});
diff --git a/src/layouts/modules/global-tab/index.vue b/src/layouts/modules/global-tab/index.vue
index 56034271..ef5bb910 100644
--- a/src/layouts/modules/global-tab/index.vue
+++ b/src/layouts/modules/global-tab/index.vue
@@ -5,7 +5,6 @@ import { useElementBounding } from '@vueuse/core';
import { PageTab } from '@sa/materials';
import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
-import { useRouteStore } from '@/store/modules/route';
import { useTabStore } from '@/store/modules/tab';
import { isPC } from '@/utils/agent';
import BetterScroll from '@/components/custom/better-scroll.vue';
@@ -18,7 +17,6 @@ defineOptions({
const route = useRoute();
const appStore = useAppStore();
const themeStore = useThemeStore();
-const routeStore = useRouteStore();
const tabStore = useTabStore();
const bsWrapper = ref();
@@ -82,12 +80,8 @@ function getContextMenuDisabledKeys(tabId: string) {
return disabledKeys;
}
-async function handleCloseTab(tab: App.Global.Tab) {
- await tabStore.removeTab(tab.id);
-
- if (themeStore.resetCacheStrategy === 'close') {
- routeStore.resetRouteCache(tab.routeKey);
- }
+function handleCloseTab(tab: App.Global.Tab) {
+ tabStore.removeTab(tab.id);
}
async function refresh() {
diff --git a/src/store/modules/tab/index.ts b/src/store/modules/tab/index.ts
index ace7d7dc..78aaac69 100644
--- a/src/store/modules/tab/index.ts
+++ b/src/store/modules/tab/index.ts
@@ -98,13 +98,22 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
const removeTabIndex = tabs.value.findIndex(tab => tab.id === tabId);
if (removeTabIndex === -1) return;
+ const removedTabRouteKey = tabs.value[removeTabIndex].routeKey;
const isRemoveActiveTab = activeTabId.value === tabId;
const nextTab = tabs.value[removeTabIndex + 1] || homeTab.value;
+ // remove tab
tabs.value.splice(removeTabIndex, 1);
+
+ // if current tab is removed, then switch to next tab
if (isRemoveActiveTab && nextTab) {
await switchRouteByTab(nextTab);
}
+
+ // reset route cache if cache strategy is close
+ if (themeStore.resetCacheStrategy === 'close') {
+ routeStore.resetRouteCache(removedTabRouteKey);
+ }
}
/** remove active tab */
@@ -131,9 +140,26 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
*/
async function clearTabs(excludes: string[] = [], clearCache: boolean = false) {
const remainTabIds = [...getFixedTabIds(tabs.value), ...excludes];
- const removedTabsIds = tabs.value.map(tab => tab.id).filter(id => !remainTabIds.includes(id));
+
+ // Identify tabs to be removed and collect their routeKeys if strategy is 'close'
+ const tabsToRemove = tabs.value.filter(tab => !remainTabIds.includes(tab.id));
+ const routeKeysToReset: RouteKey[] = [];
+
+ if (themeStore.resetCacheStrategy === 'close') {
+ for (const tab of tabsToRemove) {
+ routeKeysToReset.push(tab.routeKey);
+ }
+ }
+
+ const removedTabsIds = tabsToRemove.map(tab => tab.id);
+
+ // If no tabs are actually being removed based on excludes and fixed tabs, exit
+ if (removedTabsIds.length === 0) {
+ return;
+ }
const isRemoveActiveTab = removedTabsIds.includes(activeTabId.value);
+ // filterTabsByIds returns tabs NOT in removedTabsIds, so these are the tabs that will remain
const updatedTabs = filterTabsByIds(removedTabsIds, tabs.value);
if (clearCache) {
@@ -152,13 +178,21 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
if (!isRemoveActiveTab) {
update();
- return;
+ } else {
+ const activeTabCandidate = updatedTabs[updatedTabs.length - 1] || homeTab.value;
+
+ if (activeTabCandidate) {
+ // Ensure there's a tab to switch to
+ await switchRouteByTab(activeTabCandidate);
+ }
+ // Update the tabs array regardless of switch success or if a candidate was found
+ update();
}
- const activeTab = updatedTabs[updatedTabs.length - 1] || homeTab.value;
-
- await switchRouteByTab(activeTab);
- update();
+ // After tabs are updated and route potentially switched, reset cache for removed tabs
+ for (const routeKey of routeKeysToReset) {
+ routeStore.resetRouteCache(routeKey);
+ }
}
const { routerPushByKey } = useRouterPush();