refactor: 新增 useElapsedSeconds 统一秒表计时
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { useElapsedSeconds } from '@/components/composables/useElapsedSeconds'
|
||||
|
||||
describe('useElapsedSeconds', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
it('start 从 0 开始每秒递增', () => {
|
||||
const { seconds, start, stop } = useElapsedSeconds()
|
||||
|
||||
start()
|
||||
expect(seconds.value).toBe(0)
|
||||
|
||||
vi.advanceTimersByTime(3000)
|
||||
expect(seconds.value).toBe(3)
|
||||
stop()
|
||||
})
|
||||
|
||||
it('重复 start 会归零并重新计时', () => {
|
||||
const { seconds, start, stop } = useElapsedSeconds()
|
||||
|
||||
start()
|
||||
vi.advanceTimersByTime(5000)
|
||||
|
||||
start()
|
||||
expect(seconds.value).toBe(0)
|
||||
vi.advanceTimersByTime(1000)
|
||||
expect(seconds.value).toBe(1)
|
||||
stop()
|
||||
})
|
||||
|
||||
it('stop 停止递增但保留秒数', () => {
|
||||
const { seconds, start, stop } = useElapsedSeconds()
|
||||
|
||||
start()
|
||||
vi.advanceTimersByTime(2000)
|
||||
stop()
|
||||
expect(seconds.value).toBe(2)
|
||||
|
||||
vi.advanceTimersByTime(5000)
|
||||
expect(seconds.value).toBe(2)
|
||||
})
|
||||
|
||||
it('reset 停止并归零', () => {
|
||||
const { seconds, start, reset } = useElapsedSeconds()
|
||||
|
||||
start()
|
||||
vi.advanceTimersByTime(2000)
|
||||
reset()
|
||||
expect(seconds.value).toBe(0)
|
||||
|
||||
vi.advanceTimersByTime(5000)
|
||||
expect(seconds.value).toBe(0)
|
||||
})
|
||||
|
||||
it('在组件内使用时卸载自动清理定时器', async () => {
|
||||
const { mount } = await import('@vue/test-utils')
|
||||
const { defineComponent, h } = await import('vue')
|
||||
|
||||
let exposed!: ReturnType<typeof useElapsedSeconds>
|
||||
const Host = defineComponent({
|
||||
setup() {
|
||||
exposed = useElapsedSeconds()
|
||||
return () => h('div')
|
||||
},
|
||||
})
|
||||
const wrapper = mount(Host)
|
||||
|
||||
exposed.start()
|
||||
vi.advanceTimersByTime(2000)
|
||||
expect(exposed.seconds.value).toBe(2)
|
||||
|
||||
wrapper.unmount()
|
||||
vi.advanceTimersByTime(5000)
|
||||
expect(exposed.seconds.value).toBe(2)
|
||||
})
|
||||
})
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "@/api/standardMindMap";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import MindMapViewer, { type MindMapTreeNode } from "@/components/MindMapViewer.vue";
|
||||
import { useElapsedSeconds } from "@/components/composables/useElapsedSeconds";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -21,12 +22,20 @@ const taskNum = computed(() => route.params.taskNum as string);
|
||||
const loading = ref(false);
|
||||
const standard = ref<StandardMindMap | null>(null);
|
||||
const comparing = ref(false);
|
||||
const comparingSeconds = ref(0);
|
||||
let comparingTimer: ReturnType<typeof setInterval> | null = null;
|
||||
const {
|
||||
seconds: comparingSeconds,
|
||||
start: startComparingTimer,
|
||||
stop: stopComparingTimer,
|
||||
} = useElapsedSeconds();
|
||||
|
||||
// 重新生成标准导图
|
||||
const regenerating = ref(false);
|
||||
const regeneratingSeconds = ref(0);
|
||||
let regeneratingTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
const {
|
||||
seconds: regeneratingSeconds,
|
||||
start: startRegeneratingTimer,
|
||||
stop: stopRegeneratingTimer,
|
||||
} = useElapsedSeconds();
|
||||
const lastResult = ref<any>(null);
|
||||
const hasCompared = ref(false);
|
||||
|
||||
@@ -127,7 +136,7 @@ const loadData = async () => {
|
||||
// 匹配失败时仍可进入回忆页
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
} catch {
|
||||
standard.value = null;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
@@ -201,8 +210,7 @@ const handleRegenerate = async () => {
|
||||
|
||||
// 开始生成
|
||||
regenerating.value = true;
|
||||
regeneratingSeconds.value = 0;
|
||||
regeneratingTimer = setInterval(() => { regeneratingSeconds.value++; }, 1000);
|
||||
startRegeneratingTimer();
|
||||
try {
|
||||
const res = await regenerateStandardMindMap(taskNum.value, mode);
|
||||
standard.value = res?.data || null;
|
||||
@@ -211,7 +219,7 @@ const handleRegenerate = async () => {
|
||||
ElMessage.error(e.message || "重新生成失败");
|
||||
} finally {
|
||||
regenerating.value = false;
|
||||
if (regeneratingTimer) { clearInterval(regeneratingTimer); regeneratingTimer = null; }
|
||||
stopRegeneratingTimer();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -223,8 +231,7 @@ const handleRecallCompare = async () => {
|
||||
return;
|
||||
}
|
||||
comparing.value = true;
|
||||
comparingSeconds.value = 0;
|
||||
comparingTimer = setInterval(() => { comparingSeconds.value++; }, 1000);
|
||||
startComparingTimer();
|
||||
try {
|
||||
const res = await recallCompare(taskNum.value, outline, focusPath.value || undefined);
|
||||
standard.value = res?.data || null;
|
||||
@@ -248,7 +255,7 @@ const handleRecallCompare = async () => {
|
||||
ElMessage.error(e.message || "对比失败");
|
||||
} finally {
|
||||
comparing.value = false;
|
||||
if (comparingTimer) { clearInterval(comparingTimer); comparingTimer = null; }
|
||||
stopComparingTimer();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// src/components/composables/useElapsedSeconds.ts
|
||||
import { getCurrentInstance, onUnmounted, ref } from 'vue';
|
||||
|
||||
/**
|
||||
* 统一的秒表计时:记录异步操作已耗时秒数,用于“已等待 N 秒”等界面提示。
|
||||
* - start():归零并开始每秒递增(重复调用会先停止上一次计时)
|
||||
* - stop():停止计时,保留当前秒数
|
||||
* - reset():停止并归零
|
||||
* 组件卸载时自动清理定时器;在组件外部调用仅作纯状态机使用。
|
||||
*/
|
||||
export function useElapsedSeconds() {
|
||||
const seconds = ref(0);
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
const stop = () => {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const start = () => {
|
||||
stop();
|
||||
seconds.value = 0;
|
||||
timer = setInterval(() => {
|
||||
seconds.value++;
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
stop();
|
||||
seconds.value = 0;
|
||||
};
|
||||
|
||||
if (getCurrentInstance()) {
|
||||
onUnmounted(stop);
|
||||
}
|
||||
|
||||
return { seconds, start, stop, reset };
|
||||
}
|
||||
Reference in New Issue
Block a user