From e5adb4095b9ae53d061a0c95dab3455f94415fca Mon Sep 17 00:00:00 2001 From: cat_shark <1716967236@qq.com> Date: Sun, 5 Jul 2026 14:39:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=A4=8D=E4=B9=A0):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E7=BB=B4=E5=BA=A6=E5=A4=8D=E4=B9=A0=20+=20?= =?UTF-8?q?=E6=A0=87=E5=87=86=E5=AF=BC=E5=9B=BE=E6=8C=89=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MindMapNode 新增 sessionNum 字段,追踪节点来源会话 - BuiltinMindMapGenerator 生成节点时填充 sessionNum - MindMapTreeTool.filterBySession() 按会话过滤标准导图 - recallCompare 接受 sessionNum 参数,筛选后对比 - 新增 review_recall_records.session_num 字段(Flyway V20260705_1) - 新增 GET /study-sessions/tasks/{taskNum}/sessions 端点 - 前端 ReviewRecall.vue 增加复习范围选择器(全部/指定会话) - 历史报告条目展示学习预期目标 --- src/api/standardMindMap.ts | 9 ++-- src/api/studySessions.ts | 5 +++ src/components/ReviewRecall.vue | 74 ++++++++++++++++++++++++++++++++- src/components/StartTask.vue | 25 ++++++++++- 4 files changed, 108 insertions(+), 5 deletions(-) diff --git a/src/api/standardMindMap.ts b/src/api/standardMindMap.ts index 275a8fe..02b538f 100644 --- a/src/api/standardMindMap.ts +++ b/src/api/standardMindMap.ts @@ -23,6 +23,7 @@ export interface RecallRecord { id: number; taskNum: string; standardMapId: number; + sessionNum?: string; recallContent: string; compareResult: string; recallRatio: number; @@ -47,9 +48,11 @@ export const updateStandardMindMap = (taskNum: string, outline: string) => { return request.put(`/review/standard-mind-map/${taskNum}`, { outline }); }; -/** 用户提交回忆大纲,与标准导图对比 */ -export const recallCompare = (taskNum: string, recallOutline: string) => { - return request.post(`/review/standard-mind-map/${taskNum}/recall`, { recallOutline }); +/** 用户提交回忆大纲,与标准导图对比(可选指定会话维度) */ +export const recallCompare = (taskNum: string, recallOutline: string, sessionNum?: string) => { + const body: Record = { recallOutline }; + if (sessionNum) body.sessionNum = sessionNum; + return request.post(`/review/standard-mind-map/${taskNum}/recall`, body); }; /** 获取该任务的所有回忆对比记录 */ diff --git a/src/api/studySessions.ts b/src/api/studySessions.ts index 981a3a8..b36bf6f 100644 --- a/src/api/studySessions.ts +++ b/src/api/studySessions.ts @@ -44,6 +44,11 @@ export const getActiveSession = (excludeTaskNum?: string) => { return request.get('/study-sessions/active', params); }; +/** 获取任务的所有会话简要信息(复习维度选择用) */ +export const getTaskSessions = (taskNum: string) => { + return request.get(`/study-sessions/tasks/${taskNum}/sessions`); +}; + /** 分页查询任务的历史残片 */ export const getTaskFragments = (taskNum: string, page: number, size: number, keyword?: string) => { const params: Record = { page, size }; diff --git a/src/components/ReviewRecall.vue b/src/components/ReviewRecall.vue index 9c3ed34..6d6fb92 100644 --- a/src/components/ReviewRecall.vue +++ b/src/components/ReviewRecall.vue @@ -12,6 +12,7 @@ import { } from "@/api/standardMindMap"; import { ElMessage, ElMessageBox } from "element-plus"; import MindMapViewer, { type MindMapTreeNode } from "@/components/MindMapViewer.vue"; +import { getTaskSessions } from "@/api/studySessions"; const route = useRoute(); const router = useRouter(); @@ -25,6 +26,11 @@ let comparingTimer: ReturnType | null = null; const lastResult = ref(null); const hasCompared = ref(false); +// 复习维度选择 +const sessions = ref<{ sessionNum: string; startTime: string; expectation: string; reportCount: number; fragmentCount: number }[]>([]); +const selectedSession = ref(""); +const loadingSessions = ref(false); + // 回忆输入(思维导图形式) const recallTree = ref(null); const recallViewer = ref | null>(null); @@ -144,7 +150,7 @@ const handleRecallCompare = async () => { comparingSeconds.value = 0; comparingTimer = setInterval(() => { comparingSeconds.value++; }, 1000); try { - const res = await recallCompare(taskNum.value, outline); + const res = await recallCompare(taskNum.value, outline, selectedSession.value || undefined); standard.value = res?.data || null; hasCompared.value = true; // 解析对比结果 @@ -270,8 +276,29 @@ const extraNodes = computed(() => { return raw.map((item: any) => typeof item === "string" ? { title: item } : item); }); +// 加载会话列表(复习维度选择器用) +const loadSessions = async () => { + loadingSessions.value = true; + try { + const res = await getTaskSessions(taskNum.value); + sessions.value = res?.data || []; + } catch { + sessions.value = []; + } finally { + loadingSessions.value = false; + } +}; + +// 格式化会话标签 +const formatSessionLabel = (s: { sessionNum: string; startTime: string; expectation: string; reportCount: number; fragmentCount: number }) => { + const date = s.startTime ? s.startTime.replace("T", " ").substring(0, 16) : s.sessionNum; + const hint = s.expectation ? s.expectation.substring(0, 30) : ""; + return `${date} ${hint ? "— " + hint : ""}`; +}; + onMounted(() => { loadData(); + loadSessions(); }); @@ -286,6 +313,24 @@ onMounted(() => { + +
+ 复习范围 + + + + + + 已过滤:仅匹配该会话知识点 + + 不过滤,对比全部标准导图 +
+
@@ -317,6 +362,8 @@ onMounted(() => { @click="selectHistoryRecord(record)" > {{ record.createdTime?.replace("T", " ")?.substring(0, 16) }} + 会话 + 全部 覆盖率 {{ (record.recallRatio * 100).toFixed(1) }}% ✅{{ record.matchedCount }} ❌{{ record.missedCount }} ➕{{ record.extraCount }}
@@ -709,4 +756,29 @@ onMounted(() => { .history-detail { color: var(--text-secondary, #666); } + +/* 复习范围选择器 */ +.review-scope { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + padding: 12px 22px; +} + +.scope-label { + font-size: 13px; + font-weight: 600; + color: var(--green-800); + white-space: nowrap; +} + +.scope-hint { + font-size: 12px; + color: var(--text-secondary, #999); +} + +.session-badge { + flex-shrink: 0; +} diff --git a/src/components/StartTask.vue b/src/components/StartTask.vue index cd7790b..3adbc0d 100644 --- a/src/components/StartTask.vue +++ b/src/components/StartTask.vue @@ -677,7 +677,10 @@ onUnmounted(() => {
- {{ item.sessionNum }} +
+ {{ item.sessionNum }} + {{ item.sessionExpectation }} +
@@ -1098,6 +1101,26 @@ onUnmounted(() => { font-weight: 600; } +.report-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 4px; + flex-wrap: wrap; +} + +.expectation-hint { + font-size: 12px; + color: var(--text-secondary, #666); + background: #f5f7f5; + padding: 1px 8px; + border-radius: 4px; + white-space: nowrap; + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; +} + .history-content { flex: 1; font-size: 14px;