fix: 学习会话按钮补防抖加载
This commit is contained in:
@@ -88,4 +88,25 @@ describe('useStudyFragment', () => {
|
||||
expect(result).toBe(false)
|
||||
expect(ElMessage.error).toHaveBeenCalledWith('网络超时')
|
||||
})
|
||||
|
||||
it('prevents duplicate submission while creating fragment', async () => {
|
||||
let resolveCreate!: (value: any) => void
|
||||
vi.mocked(createFragments).mockImplementation(
|
||||
() => new Promise((resolve) => { resolveCreate = resolve }),
|
||||
)
|
||||
const { fragmentContent, creatingFragment, confirmGenerateFragment } = useStudyFragment()
|
||||
fragmentContent.value = '学习内容'
|
||||
|
||||
const first = confirmGenerateFragment('S001')
|
||||
const second = confirmGenerateFragment('S001')
|
||||
await Promise.resolve()
|
||||
|
||||
expect(createFragments).toHaveBeenCalledTimes(1)
|
||||
expect(creatingFragment.value).toBe(true)
|
||||
|
||||
resolveCreate({ code: 200 })
|
||||
await Promise.all([first, second])
|
||||
|
||||
expect(creatingFragment.value).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -25,9 +25,12 @@ const {
|
||||
openFragmentDialog,
|
||||
closeFragmentDialog,
|
||||
confirmGenerateFragment,
|
||||
creatingFragment,
|
||||
} = useStudyFragment();
|
||||
|
||||
const pageLoading = ref(true);
|
||||
const timerActionLoading = ref(false);
|
||||
const endingSession = ref(false);
|
||||
|
||||
const summaryDialogVisible = ref(false);
|
||||
const summaryContent = ref("");
|
||||
@@ -309,39 +312,51 @@ const statusText = computed(() => {
|
||||
});
|
||||
|
||||
const startTimer = async () => {
|
||||
if (taskInfo.value.sessionState === "PAUSED") {
|
||||
const res = await continueSession(taskInfo.value.sessionNum);
|
||||
if (res.code === 200) ElMessage.success("任务继续");
|
||||
await loadTaskSession();
|
||||
if (timerActionLoading.value) return;
|
||||
timerActionLoading.value = true;
|
||||
try {
|
||||
if (taskInfo.value.sessionState === "PAUSED") {
|
||||
const res = await continueSession(taskInfo.value.sessionNum);
|
||||
if (res.code === 200) ElMessage.success("任务继续");
|
||||
await loadTaskSession();
|
||||
}
|
||||
// 手动开始 → 清除休息状态
|
||||
localStorage.removeItem(BREAK_END_KEY.value);
|
||||
isBreak.value = false;
|
||||
const duration = taskInfo.value.pointerPosition || 25 * 60 * 1000;
|
||||
runCountdown(duration);
|
||||
} finally {
|
||||
timerActionLoading.value = false;
|
||||
}
|
||||
// 手动开始 → 清除休息状态
|
||||
localStorage.removeItem(BREAK_END_KEY.value);
|
||||
isBreak.value = false;
|
||||
const duration = taskInfo.value.pointerPosition || 25 * 60 * 1000;
|
||||
runCountdown(duration);
|
||||
};
|
||||
|
||||
const stopTimer = async () => {
|
||||
const res = await pauseSession(taskInfo.value.sessionNum);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("任务暂停");
|
||||
// 重新获取会话数据,同步后端计算后的时间字段
|
||||
try {
|
||||
const detail = await getSessionDetail(taskInfo.value.sessionNum);
|
||||
if (detail?.data) {
|
||||
const d = detail.data;
|
||||
taskInfo.value.sessionState = d.sessionState ?? "PAUSED";
|
||||
taskInfo.value.actualTime = d.actualTime ?? 0;
|
||||
taskInfo.value.effectiveTime = d.effectiveTime ?? 0;
|
||||
taskInfo.value.effectivenessRatio = d.effectivenessRatio ?? 0;
|
||||
taskInfo.value.startTime = d.startTime ?? taskInfo.value.startTime;
|
||||
taskInfo.value.endTime = d.endTime ?? "";
|
||||
taskInfo.value.lastStartTime = d.lastStartTime ?? "";
|
||||
taskInfo.value.pointerPosition = d.pointerPosition ?? 0;
|
||||
if (timerActionLoading.value) return;
|
||||
timerActionLoading.value = true;
|
||||
try {
|
||||
const res = await pauseSession(taskInfo.value.sessionNum);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("任务暂停");
|
||||
// 重新获取会话数据,同步后端计算后的时间字段
|
||||
try {
|
||||
const detail = await getSessionDetail(taskInfo.value.sessionNum);
|
||||
if (detail?.data) {
|
||||
const d = detail.data;
|
||||
taskInfo.value.sessionState = d.sessionState ?? "PAUSED";
|
||||
taskInfo.value.actualTime = d.actualTime ?? 0;
|
||||
taskInfo.value.effectiveTime = d.effectiveTime ?? 0;
|
||||
taskInfo.value.effectivenessRatio = d.effectivenessRatio ?? 0;
|
||||
taskInfo.value.startTime = d.startTime ?? taskInfo.value.startTime;
|
||||
taskInfo.value.endTime = d.endTime ?? "";
|
||||
taskInfo.value.lastStartTime = d.lastStartTime ?? "";
|
||||
taskInfo.value.pointerPosition = d.pointerPosition ?? 0;
|
||||
}
|
||||
} catch {
|
||||
taskInfo.value.sessionState = "PAUSED";
|
||||
}
|
||||
} catch {
|
||||
taskInfo.value.sessionState = "PAUSED";
|
||||
}
|
||||
} finally {
|
||||
timerActionLoading.value = false;
|
||||
}
|
||||
clear();
|
||||
clearBreakState();
|
||||
@@ -367,6 +382,8 @@ const endTimer = async (content: string) => {
|
||||
// 用户取消结束会话,不做任何操作
|
||||
return;
|
||||
}
|
||||
if (endingSession.value) return;
|
||||
endingSession.value = true;
|
||||
|
||||
try {
|
||||
const res = await endSession(taskInfo.value.sessionNum, content);
|
||||
@@ -385,6 +402,8 @@ const endTimer = async (content: string) => {
|
||||
await router.push("/study");
|
||||
} catch {
|
||||
ElMessage.error("结束会话失败,请重试");
|
||||
} finally {
|
||||
endingSession.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -741,10 +760,10 @@ defineExpose({
|
||||
<el-button @click="openFragmentDialog">生成残片</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button type="success" @click="startTimer" :disabled="timerRunning">开始</el-button>
|
||||
<el-button @click="stopTimer" :disabled="!timerRunning" v-if="!timerIsOver">暂停</el-button>
|
||||
<el-button type="success" @click="startTimer" :disabled="timerRunning" :loading="timerActionLoading">开始</el-button>
|
||||
<el-button @click="stopTimer" :disabled="!timerRunning" :loading="timerActionLoading" v-if="!timerIsOver">暂停</el-button>
|
||||
<el-button @click="openFragmentDialog">生成残片</el-button>
|
||||
<el-button v-if="timerIsOver" plain type="success" @click="restTimer">休息</el-button>
|
||||
<el-button v-if="timerIsOver" plain type="success" @click="restTimer" :loading="timerActionLoading">休息</el-button>
|
||||
</template>
|
||||
<el-button type="danger" plain @click="openSummaryDialog">结束会话</el-button>
|
||||
</div>
|
||||
@@ -827,7 +846,7 @@ defineExpose({
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="closeFragmentDialog">取消</el-button>
|
||||
<el-button type="success" @click="confirmGenerateFragment(taskInfo.sessionNum)">确定生成</el-button>
|
||||
<el-button type="success" :loading="creatingFragment" @click="confirmGenerateFragment(taskInfo.sessionNum)">确定生成</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
@@ -887,7 +906,7 @@ defineExpose({
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="closeSummaryDialog">取消</el-button>
|
||||
<el-button type="success" @click="endTimer(summaryContent)">确认结束</el-button>
|
||||
<el-button type="success" :loading="endingSession" @click="endTimer(summaryContent)">确认结束</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {createFragments} from '@/api/reportFragments';
|
||||
export const useStudyFragment = () => {
|
||||
const fragmentsDialogVisible = ref(false);
|
||||
const fragmentContent = ref('');
|
||||
const creatingFragment = ref(false);
|
||||
|
||||
const openFragmentDialog = () => {
|
||||
fragmentContent.value = '';
|
||||
@@ -26,6 +27,7 @@ export const useStudyFragment = () => {
|
||||
};
|
||||
|
||||
const confirmGenerateFragment = async (sessionNum: string): Promise<boolean> => {
|
||||
if (creatingFragment.value) return false;
|
||||
if (!fragmentContent.value.trim()) {
|
||||
ElMessage.warning('请输入学习内容!');
|
||||
return false;
|
||||
@@ -43,6 +45,7 @@ export const useStudyFragment = () => {
|
||||
}
|
||||
|
||||
try {
|
||||
creatingFragment.value = true;
|
||||
const res = await createFragments(sessionNum, fragmentContent.value);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('学习残片生成成功!');
|
||||
@@ -55,12 +58,15 @@ export const useStudyFragment = () => {
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || '请求失败');
|
||||
return false;
|
||||
} finally {
|
||||
creatingFragment.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
fragmentsDialogVisible,
|
||||
fragmentContent,
|
||||
creatingFragment,
|
||||
openFragmentDialog,
|
||||
closeFragmentDialog,
|
||||
confirmGenerateFragment,
|
||||
|
||||
Reference in New Issue
Block a user