feat: refine topic insights and learner experience

This commit is contained in:
2026-08-17 12:42:16 +08:00
parent 77399df060
commit 833763c461
27 changed files with 848 additions and 182 deletions

View File

@@ -25,7 +25,7 @@ const loadingSessions = ref(false);
const sessionOperationPending = ref(false);
const logoutDialogOpen = ref(false);
const personalCenterOpen = ref(false);
const personalCenterSection = ref<"overview" | "review" | "records">("overview");
const personalCenterSection = ref<"overview" | "review" | "reports" | "records">("overview");
const helpCardDialogOpen = ref(false);
const shareDraftDialogOpen = ref(false);
const practiceReview = ref<PracticeReviewResult | null>(null);
@@ -38,7 +38,6 @@ const shareDraftHistory = ref<ShareDraft[]>([]);
const reportHistory = ref<PeriodicReport[]>([]);
const personalCenterLoading = ref(false);
const cardOperationPending = ref(false);
const finishingTopic = ref(false);
const generatingHelpCard = ref(false);
const generatingShareDraft = ref(false);
const statusText = ref("连接后端中");
@@ -51,7 +50,6 @@ const feedbackContent = ref("");
const feedbackSubmitting = ref(false);
const activeAbortController = ref<AbortController | null>(null);
let toastTimer: number | null = null;
let settlementPollVersion = 0;
onMounted(bootstrap);
@@ -96,7 +94,6 @@ async function bootstrap() {
}
onBeforeUnmount(() => {
settlementPollVersion += 1;
activeAbortController.value?.abort();
if (toastTimer) window.clearTimeout(toastTimer);
document.body.classList.remove("drawer-open");
@@ -129,14 +126,13 @@ async function loadSessions() {
async function createSession() {
if (sessionOperationPending.value) return;
composerKey.value += 1;
const existingBlank = sessions.value.find((session) => session.messageCount === 0);
if (existingBlank) {
await selectSession(existingBlank.id);
const activeSession = sessions.value.find((session) => session.id === activeSessionId.value);
if (activeSession?.messageCount === 0) {
return;
}
sessionOperationPending.value = true;
try {
const result = await api.createSession();
const result = await api.createSession(activeSessionId.value ?? undefined);
sessions.value = await api.listSessions();
await selectSession(result.sessionId, true);
} catch (error) {
@@ -306,48 +302,6 @@ async function stop() {
}
}
async function finishCurrentTopic() {
if (!activeSessionId.value || sending.value || finishingTopic.value) return;
finishingTopic.value = true;
try {
const result = await api.finishTopic(activeSessionId.value);
if (result.settlementStatus === "success" || result.settlementStatus === "fallback") {
showToast(result.growthProfileEnabled ? "本主题已沉淀,并更新了近期实修回顾" : "本主题已沉淀");
} else {
showToast("主题已结束,实修记录正在后台沉淀,可以继续使用其他功能");
void watchTopicSettlement(result.summary.id);
}
await refreshSessionList();
await refreshProfile();
} catch (error) {
handleError(error, "主题沉淀失败");
} finally {
finishingTopic.value = false;
}
}
async function watchTopicSettlement(summaryId: number) {
const version = ++settlementPollVersion;
for (let attempt = 0; attempt < 40; attempt += 1) {
await new Promise((resolve) => window.setTimeout(resolve, 3000));
if (version !== settlementPollVersion || !user.value) return;
try {
const result = await api.topicSettlement(summaryId);
if (result.settlementStatus === "success" || result.settlementStatus === "fallback") {
showToast(result.growthProfileEnabled ? "实修记录已沉淀,并更新了近期实修回顾" : "实修记录已沉淀完成");
await refreshProfile();
return;
}
if (result.settlementStatus === "failed") {
showToast("实修记录暂时沉淀失败,系统已保留对话,可稍后重试");
return;
}
} catch {
// 轮询失败不影响聊天;下一轮继续查询持久化任务状态。
}
}
}
async function generateHelpCard() {
if (!activeSessionId.value || sending.value || generatingHelpCard.value) return;
generatingHelpCard.value = true;
@@ -470,7 +424,7 @@ async function deleteHistoryShareDraft(draftId: number, done: (success: boolean)
}
}
async function openPersonalCenter(section: "overview" | "review" | "records" = "overview") {
async function openPersonalCenter(section: "overview" | "review" | "reports" | "records" = "overview") {
personalCenterSection.value = section;
personalCenterOpen.value = true;
personalCenterLoading.value = true;
@@ -629,11 +583,8 @@ async function copyText(text: string) {
:used="user.todayUsed"
:limit="user.dailyLimit"
:entitlement="user.entitlement"
:finishing="finishingTopic"
:generating-help-card="generatingHelpCard"
:generating-share-draft="generatingShareDraft"
@finish-topic="finishCurrentTopic"
@open-profile="openPersonalCenter('review')"
@generate-help-card="generateHelpCard"
@generate-share-draft="generateShareDraft"
/>