feat(前端):AI 操作超时 + 加载提示
- axios 全局超时 5s → 180s(3 分钟),避免 AI 响应未到即断开 - 回忆对比按钮等待时显示旋转动画 + 已等待秒数 + 30s 后安抚文案 - 结束会话报告草稿生成时显示等待提示 + 秒数统计
This commit is contained in:
@@ -20,6 +20,8 @@ const taskNum = computed(() => route.params.taskNum as string);
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const standard = ref<StandardMindMap | null>(null);
|
const standard = ref<StandardMindMap | null>(null);
|
||||||
const comparing = ref(false);
|
const comparing = ref(false);
|
||||||
|
const comparingSeconds = ref(0);
|
||||||
|
let comparingTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
const lastResult = ref<any>(null);
|
const lastResult = ref<any>(null);
|
||||||
const hasCompared = ref(false);
|
const hasCompared = ref(false);
|
||||||
|
|
||||||
@@ -139,6 +141,8 @@ const handleRecallCompare = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
comparing.value = true;
|
comparing.value = true;
|
||||||
|
comparingSeconds.value = 0;
|
||||||
|
comparingTimer = setInterval(() => { comparingSeconds.value++; }, 1000);
|
||||||
try {
|
try {
|
||||||
const res = await recallCompare(taskNum.value, outline);
|
const res = await recallCompare(taskNum.value, outline);
|
||||||
standard.value = res?.data || null;
|
standard.value = res?.data || null;
|
||||||
@@ -162,6 +166,7 @@ const handleRecallCompare = async () => {
|
|||||||
ElMessage.error(e.message || "对比失败");
|
ElMessage.error(e.message || "对比失败");
|
||||||
} finally {
|
} finally {
|
||||||
comparing.value = false;
|
comparing.value = false;
|
||||||
|
if (comparingTimer) { clearInterval(comparingTimer); comparingTimer = null; }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -328,6 +333,10 @@ onMounted(() => {
|
|||||||
清空重写
|
清空重写
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
<p v-if="comparing" class="ai-waiting">
|
||||||
|
AI 正在对比分析中,已等待 {{ comparingSeconds }} 秒…
|
||||||
|
<template v-if="comparingSeconds > 30">内容较多,请耐心等待</template>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="panel-hint">
|
<p class="panel-hint">
|
||||||
凭记忆在导图上补全该任务的知识点:选中节点后 Tab 加子节点、Enter 加同级节点、双击修改文字。
|
凭记忆在导图上补全该任务的知识点:选中节点后 Tab 加子节点、Enter 加同级节点、双击修改文字。
|
||||||
@@ -530,6 +539,29 @@ onMounted(() => {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-waiting {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #e6a23c;
|
||||||
|
margin: 4px 0 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-waiting::before {
|
||||||
|
content: "";
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border: 2px solid #f3d19e;
|
||||||
|
border-top-color: #e6a23c;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
.standard-meta {
|
.standard-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ const {
|
|||||||
const summaryDialogVisible = ref(false);
|
const summaryDialogVisible = ref(false);
|
||||||
const summaryContent = ref("");
|
const summaryContent = ref("");
|
||||||
const summaryLoading = ref(false);
|
const summaryLoading = ref(false);
|
||||||
|
const summaryLoadingSeconds = ref(0);
|
||||||
|
let summaryLoadingTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
|
|
||||||
// 学习预期
|
// 学习预期
|
||||||
const expectationDialogVisible = ref(false);
|
const expectationDialogVisible = ref(false);
|
||||||
@@ -240,6 +242,8 @@ const openSummaryDialog = async () => {
|
|||||||
// 有残片且未填写过总结时,用 AI 聚合草稿作为编辑起点
|
// 有残片且未填写过总结时,用 AI 聚合草稿作为编辑起点
|
||||||
if (!summaryContent.value.trim() && fragmentsList.value.length > 0) {
|
if (!summaryContent.value.trim() && fragmentsList.value.length > 0) {
|
||||||
summaryLoading.value = true;
|
summaryLoading.value = true;
|
||||||
|
summaryLoadingSeconds.value = 0;
|
||||||
|
summaryLoadingTimer = setInterval(() => { summaryLoadingSeconds.value++; }, 1000);
|
||||||
try {
|
try {
|
||||||
const res = await getReportDraft(taskInfo.value.sessionNum);
|
const res = await getReportDraft(taskInfo.value.sessionNum);
|
||||||
if (res?.code === 200 && res.data) {
|
if (res?.code === 200 && res.data) {
|
||||||
@@ -249,12 +253,14 @@ const openSummaryDialog = async () => {
|
|||||||
// 草稿失败不阻塞手写
|
// 草稿失败不阻塞手写
|
||||||
} finally {
|
} finally {
|
||||||
summaryLoading.value = false;
|
summaryLoading.value = false;
|
||||||
|
if (summaryLoadingTimer) { clearInterval(summaryLoadingTimer); summaryLoadingTimer = null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeSummaryDialog = () => {
|
const closeSummaryDialog = () => {
|
||||||
summaryDialogVisible.value = false;
|
summaryDialogVisible.value = false;
|
||||||
|
if (summaryLoadingTimer) { clearInterval(summaryLoadingTimer); summaryLoadingTimer = null; }
|
||||||
};
|
};
|
||||||
|
|
||||||
const restTimer = async () => {
|
const restTimer = async () => {
|
||||||
@@ -586,6 +592,9 @@ onUnmounted(() => {
|
|||||||
<span class="expectation-label">本次预期</span>
|
<span class="expectation-label">本次预期</span>
|
||||||
<span>{{ expectationSaved }}</span>
|
<span>{{ expectationSaved }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<p v-if="summaryLoading" class="ai-waiting">
|
||||||
|
AI 正在聚合残片生成草稿,已等待 {{ summaryLoadingSeconds }} 秒…
|
||||||
|
</p>
|
||||||
<el-input
|
<el-input
|
||||||
v-loading="summaryLoading"
|
v-loading="summaryLoading"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
@@ -644,6 +653,29 @@ onUnmounted(() => {
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-waiting {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #e6a23c;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-waiting::before {
|
||||||
|
content: "";
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border: 2px solid #f3d19e;
|
||||||
|
border-top-color: #e6a23c;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
.summary-expectation {
|
.summary-expectation {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const basic_url = import.meta.env.VITE_BASE_URL;
|
|||||||
|
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
baseURL: basic_url,
|
baseURL: basic_url,
|
||||||
timeout: 5000,
|
timeout: 180000,
|
||||||
withCredentials: true
|
withCredentials: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user