From e65b800e01c2bb0a22d47c86f134c96564ca45ce Mon Sep 17 00:00:00 2001 From: cat-win Date: Wed, 8 Jul 2026 23:56:03 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=AF=BC=E5=9B=BE=20+=20=E4=BC=98=E5=8C=96=E5=A4=8D=E4=B9=A0?= =?UTF-8?q?=E5=BC=95=E5=AF=BC=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 ReviewMindMap 接口及 upload/get/upsert API - 删除 ReviewDetail 上传 UI 和思维导图预览 - 回忆复习按钮移至内容元信息区 - 引导提示改为渐进式:折叠时引导展开,展开后引导点击节点 - 提示语用告知语气,无表情符号 Co-Authored-By: Claude Fable 5 --- src/__tests__/api/review.spec.ts | 30 ---- src/api/review.ts | 42 ------ src/components/ReviewDetail.vue | 231 ++----------------------------- src/components/ReviewRecall.vue | 27 +++- 4 files changed, 31 insertions(+), 299 deletions(-) diff --git a/src/__tests__/api/review.spec.ts b/src/__tests__/api/review.spec.ts index 9d90465..8ade80b 100644 --- a/src/__tests__/api/review.spec.ts +++ b/src/__tests__/api/review.spec.ts @@ -14,12 +14,9 @@ import { getFragmentDetail, getReportDetail, getReviewFeed, - getReviewMindMap, getReviewTaskStats, getReviewTaskStatsByTask, getTaskReview, - upsertReviewMindMap, - uploadReviewMindMap, } from '@/api/review' const mockedRequest = vi.mocked(request) @@ -79,31 +76,4 @@ describe('review API', () => { expect(mockedRequest.get).toHaveBeenCalledWith('/review/task/T001') }) - it('review mind map API calls expected endpoints', async () => { - mockedRequest.get.mockResolvedValue({ code: 200, data: null }) - mockedRequest.put.mockResolvedValue({ code: 200, data: { id: 1 } }) - mockedRequest.post.mockResolvedValue({ code: 200, data: { id: 2 } }) - - await getReviewMindMap('T001') - expect(mockedRequest.get).toHaveBeenCalledWith('/review/mind-map/T001') - - await upsertReviewMindMap('T001', { - title: 'Map', - content: 'content', - contentFormat: 'TEXT', - }) - expect(mockedRequest.put).toHaveBeenCalledWith('/review/mind-map/T001', { - title: 'Map', - content: 'content', - contentFormat: 'TEXT', - }) - - const file = new File(['# Java'], 'java.md', { type: 'text/markdown' }) - await uploadReviewMindMap('T001', file) - expect(mockedRequest.post).toHaveBeenCalledWith( - '/review/mind-map/T001/upload', - expect.any(FormData), - { headers: { 'Content-Type': 'multipart/form-data' } }, - ) - }) }) diff --git a/src/api/review.ts b/src/api/review.ts index 848698a..a82bca9 100644 --- a/src/api/review.ts +++ b/src/api/review.ts @@ -23,25 +23,6 @@ export interface ReviewTaskStats { avgEffectivenessRatio: number; } -export interface ReviewMindMap { - id: number; - taskNum: string; - title: string; - content: string; - contentFormat: "TEXT" | "MERMAID" | "JSON"; - sourceType?: "MANUAL" | "FILE"; - fileName?: string; - filePath?: string; - fileFormat?: "XMIND" | "MARKDOWN" | "OPML" | "FREEMIND" | "TEXT"; - parsedContent?: string; - parseStatus?: "SUCCESS" | "FAILED"; - parseError?: string; - summary?: string; - lastParsedTime?: string; - createdTime?: string; - lastModifiedTime?: string; -} - export const getReviewFeed = (limit: number = 30, mode: "recent" | "random" | "smart" = "recent") => { return request.get("/review/feed", { limit, mode }); }; @@ -65,26 +46,3 @@ export const getFragmentDetail = (id: number) => { export const getTaskReview = (taskNum: string) => { return request.get(`/review/task/${taskNum}`); }; - -export const getReviewMindMap = (taskNum: string) => { - return request.get(`/review/mind-map/${taskNum}`); -}; - -export const upsertReviewMindMap = ( - taskNum: string, - payload: { - title: string; - content: string; - contentFormat?: ReviewMindMap["contentFormat"]; - }, -) => { - return request.put(`/review/mind-map/${taskNum}`, payload); -}; - -export const uploadReviewMindMap = (taskNum: string, file: File) => { - const formData = new FormData(); - formData.append("file", file); - return request.post(`/review/mind-map/${taskNum}/upload`, formData, { - headers: { "Content-Type": "multipart/form-data" }, - }); -}; diff --git a/src/components/ReviewDetail.vue b/src/components/ReviewDetail.vue index 5a2d4ca..44b024f 100644 --- a/src/components/ReviewDetail.vue +++ b/src/components/ReviewDetail.vue @@ -4,11 +4,8 @@ import { useRoute, useRouter } from "vue-router"; import { getFragmentDetail, getReportDetail, - getReviewMindMap, getTaskReview, - uploadReviewMindMap, type ReviewFeedItem, - type ReviewMindMap, } from "@/api/review"; import { getSessionDetail } from "@/api/studySessions"; import { updateFragments } from "@/api/reportFragments"; @@ -50,9 +47,6 @@ const isEditing = ref(false); const editContent = ref(""); const saving = ref(false); -const mindMap = ref(null); -const mindMapUploading = ref(false); - const sessionInfo = ref<{ taskName: string; sessionNum: string; @@ -69,40 +63,6 @@ const taskSessions = ref<{ fragments: ReviewFeedItem[]; }[]>([]); -type MindMapNode = { - title?: string; - notes?: string; - children?: MindMapNode[]; -}; - -const flattenedMindMapNodes = computed(() => { - const parsedContent = mindMap.value?.parsedContent; - if (!parsedContent) { - return []; - } - try { - const parsed = JSON.parse(parsedContent) as MindMapNode & { nodes?: MindMapNode[] }; - const rows: { title: string; level: number }[] = []; - const walk = (node: MindMapNode, level: number) => { - if (node.title) { - rows.push({ title: node.title, level }); - } - (node.children || []).forEach(child => walk(child, level + 1)); - }; - rows.push({ title: parsed.title || mindMap.value?.title || "思维导图", level: 0 }); - (parsed.nodes || parsed.children || []).forEach(node => walk(node, 1)); - return rows; - } catch { - return []; - } -}); - -const mindMapStatusType = computed(() => { - if (mindMap.value?.parseStatus === "FAILED") return "danger"; - if (mindMap.value?.parseStatus === "SUCCESS") return "success"; - return "info"; -}); - const formatDuration = (seconds?: number): string => { if (seconds == null) return "-"; const h = Math.floor(seconds / 3600); @@ -149,12 +109,6 @@ const loadTaskHistory = async (tn: string) => { } }; -const loadReviewExtensions = async (tn: string) => { - const mindMapRes = await getReviewMindMap(tn).catch(() => null); - const loadedMindMap: ReviewMindMap | null = mindMapRes?.data || null; - mindMap.value = loadedMindMap; -}; - const loadDetail = async () => { loading.value = true; isEditing.value = false; @@ -164,7 +118,6 @@ const loadDetail = async () => { taskNum.value = ""; sessionInfo.value = null; taskSessions.value = []; - mindMap.value = null; try { let res; if (contentType.value === "report") { @@ -187,7 +140,6 @@ const loadDetail = async () => { if (info?.taskNum) { await loadTaskHistory(info.taskNum); - await loadReviewExtensions(info.taskNum); } } catch { // 非关键数据 @@ -241,29 +193,6 @@ const saveEdit = async () => { } }; -const handleMindMapFileChange = async (uploadFile: any) => { - if (!taskNum.value) { - ElMessage.warning("未找到关联任务"); - return; - } - const file = uploadFile.raw as File | undefined; - if (!file) { - return; - } - mindMapUploading.value = true; - try { - const res = await uploadReviewMindMap(taskNum.value, file); - mindMap.value = res?.data || null; - if (mindMap.value?.parseStatus === "FAILED") { - ElMessage.warning("文件已上传,但解析失败"); - } else { - ElMessage.success("思维导图已解析"); - } - } finally { - mindMapUploading.value = false; - } -}; - watch( () => [contentType.value, contentId.value], () => { @@ -299,6 +228,14 @@ watch( > 编辑 + + 回忆复习 +
实际:{{ formatDuration(sessionInfo.actualTime) }} @@ -328,59 +265,6 @@ watch( -
-
-
-

思维导图

- - {{ mindMap?.parseStatus || "未上传" }} - -
- -
-
- - 上传导图文件 - - - 回忆复习 - - - {{ mindMap.fileFormat }} - -
- -
-
- {{ mindMap.title }} - {{ mindMap.fileName }} -
-

{{ mindMap.summary }}

-

- {{ mindMap.parseError || "解析失败" }} -

-
-
- {{ node.title }} -
-
-
{{ mindMap.content }}
-
- -
-
-
-

该任务下的所有学习记录

@@ -486,101 +370,6 @@ watch( padding: 40px 0; } -.review-extension-grid { - display: grid; - grid-template-columns: minmax(0, 1fr); - gap: 14px; -} - -.extension-card { - padding: 20px 22px; -} - -.extension-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - margin-bottom: 14px; -} - -.extension-head h3 { - margin: 0; - color: var(--green-900); - font-size: 16px; -} - -.mind-map-form { - display: flex; - flex-direction: column; - gap: 10px; -} - -.mind-map-upload-row { - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; -} - -.mind-map-preview { - border: 1px solid var(--border-soft); - border-radius: 8px; - background: #fbfefc; - padding: 12px; -} - -.mind-map-meta { - display: flex; - flex-direction: column; - gap: 4px; -} - -.mind-map-meta strong { - color: var(--text-primary); - word-break: break-word; -} - -.mind-map-meta span, -.mind-map-summary { - color: var(--text-secondary); - font-size: 13px; - word-break: break-word; -} - -.mind-map-summary, -.mind-map-error { - margin: 8px 0 0; -} - -.mind-map-error { - color: #c62828; - font-size: 13px; - word-break: break-word; -} - -.mind-map-outline { - margin-top: 10px; - display: flex; - flex-direction: column; - gap: 4px; -} - -.mind-map-node { - font-size: 13px; - line-height: 1.5; - color: var(--text-primary); - word-break: break-word; -} - -.mind-map-content { - margin: 10px 0 0; - white-space: pre-wrap; - word-break: break-word; - font-size: 13px; - color: var(--text-primary); -} - /* 历史记录卡片 */ .history-card { padding: 20px 22px; @@ -692,10 +481,6 @@ watch( } @media (max-width: 900px) { - .review-extension-grid { - grid-template-columns: 1fr; - } - .form-row { flex-direction: column; align-items: stretch; diff --git a/src/components/ReviewRecall.vue b/src/components/ReviewRecall.vue index 7bf2305..9e6f3c0 100644 --- a/src/components/ReviewRecall.vue +++ b/src/components/ReviewRecall.vue @@ -418,6 +418,11 @@ onMounted(() => {
+ +
+

展开下方「标准思维导图」后,可点击节点选择复习起点,然后在此凭记忆补全知识点。

+
+
@@ -509,8 +514,8 @@ onMounted(() => { 来源:{{ standard.generator }} 参考:{{ standard.sourceReportCount }} 份报告 + {{ standard.sourceFragmentCount }} 份残片
-

- 点击导图节点选择复习起点,选好后开始回忆填空 +

+ 点击导图节点即可选择复习起点,选好后凭记忆在上方补全知识点。

{ flex-shrink: 0; } +.start-guide { + padding: 14px 22px; + background: linear-gradient(135deg, #fff8e1 0%, #fff3cd 100%); + border-left: 4px solid #ffc107; + border-radius: 8px; + font-size: 14px; + line-height: 1.6; + color: #856404; +} + +.start-guide p { + margin: 0; +} + .select-hint { font-size: 13px; - color: #e6a23c; + color: #856404; margin: 0 0 8px; padding: 6px 12px; background: #fdf6ec; border-radius: 4px; - border-left: 3px solid #e6a23c; + border-left: 3px solid #ffc107; }