feat(复习):滚动条回忆卡片——先回忆再对照
- 点击滚动内容不再直接跳详情,先弹回忆卡片展示单条片段 - 用户自行回忆后点击'展开对照'查看该会话全部报告/残片 - 滚动 feed 切换为 smart 模式(时间衰减+掌握度加权) - 引入主动回忆环节,将 incidental exposure 转化为 testing effect
This commit is contained in:
Generated
+1851
-7
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
"axios": "^1.7.7",
|
||||
"css-loader": "^7.1.2",
|
||||
"element-plus": "^2.8.7",
|
||||
"mind-elixir": "^5.13.0",
|
||||
"normalize": "^0.3.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"style-loader": "^4.0.0",
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ export interface ReviewMindMap {
|
||||
lastModifiedTime?: string;
|
||||
}
|
||||
|
||||
export const getReviewFeed = (limit: number = 30, mode: "recent" | "random" = "recent") => {
|
||||
export const getReviewFeed = (limit: number = 30, mode: "recent" | "random" | "smart" = "recent") => {
|
||||
return request.get("/review/feed", { limit, mode });
|
||||
};
|
||||
|
||||
|
||||
+126
-1
@@ -63,14 +63,39 @@ const duplicatedGroups = computed(() => [...sessionGroups.value, ...sessionGroup
|
||||
|
||||
const loadReviewFeed = async () => {
|
||||
try {
|
||||
const res = await getReviewFeed(100, "random");
|
||||
const res = await getReviewFeed(100, "smart");
|
||||
reviewItems.value = res?.data || [];
|
||||
} catch {
|
||||
// feed 加载失败不影响页面主体功能
|
||||
}
|
||||
};
|
||||
|
||||
// 回忆卡片:点击滚动内容先尝试回忆,再看原文
|
||||
const recallCard = ref<{ visible: boolean; group: SessionGroup | null; revealed: boolean }>({
|
||||
visible: false,
|
||||
group: null,
|
||||
revealed: false,
|
||||
});
|
||||
|
||||
// 当前回忆卡片对应会话的全部记录
|
||||
const recallSessionItems = computed(() => {
|
||||
const group = recallCard.value.group;
|
||||
if (!group) return [];
|
||||
return reviewItems.value.filter((i) => i.sessionNum === group.sessionNum);
|
||||
});
|
||||
|
||||
const goToReviewDetail = (group: SessionGroup) => {
|
||||
recallCard.value = { visible: true, group, revealed: false };
|
||||
};
|
||||
|
||||
const revealRecallContent = () => {
|
||||
recallCard.value.revealed = true;
|
||||
};
|
||||
|
||||
const openRecallDetail = () => {
|
||||
const group = recallCard.value.group;
|
||||
if (!group) return;
|
||||
recallCard.value.visible = false;
|
||||
router.push(`/review/detail/${group.navigateType}/${group.navigateId}`);
|
||||
};
|
||||
|
||||
@@ -150,6 +175,40 @@ onMounted(() => {
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 回忆卡片:先回忆,再对照 -->
|
||||
<el-dialog
|
||||
v-model="recallCard.visible"
|
||||
:title="recallCard.group?.taskName || '回忆一下'"
|
||||
width="560px"
|
||||
>
|
||||
<template v-if="recallCard.group">
|
||||
<p class="recall-question">
|
||||
这次学习共有 {{ recallSessionItems.length }} 条记录。看着下面这个片段,先试着回忆:当时还学了什么?
|
||||
</p>
|
||||
<blockquote class="recall-snippet">{{ recallCard.group.content }}</blockquote>
|
||||
|
||||
<div v-if="recallCard.revealed" class="recall-full-list">
|
||||
<div
|
||||
v-for="item in recallSessionItems"
|
||||
:key="`${item.sourceType}-${item.id}`"
|
||||
class="recall-full-item"
|
||||
>
|
||||
<span class="recall-item-tag" :class="item.sourceType === 'REPORT' ? 'tag-report' : 'tag-fragment'">
|
||||
{{ item.sourceType === "REPORT" ? "报告" : "残片" }}
|
||||
</span>
|
||||
<span class="recall-item-content">{{ item.content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button v-if="!recallCard.revealed" type="success" @click="revealRecallContent">
|
||||
回忆好了,展开对照
|
||||
</el-button>
|
||||
<el-button v-else type="success" @click="openRecallDetail">进入详情页</el-button>
|
||||
<el-button @click="recallCard.visible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -325,6 +384,72 @@ onMounted(() => {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 回忆卡片 */
|
||||
.recall-question {
|
||||
margin: 0 0 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.recall-snippet {
|
||||
margin: 0 0 14px;
|
||||
padding: 12px 16px;
|
||||
border-left: 3px solid var(--green-600);
|
||||
background: #f1f8e9;
|
||||
border-radius: 0 6px 6px 0;
|
||||
font-size: 14px;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.7;
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.recall-full-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.recall-full-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
background: #fbfefc;
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.recall-item-tag {
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tag-report {
|
||||
background: #e8f5e9;
|
||||
color: var(--green-700);
|
||||
}
|
||||
|
||||
.tag-fragment {
|
||||
background: #fff8e1;
|
||||
color: #b8860b;
|
||||
}
|
||||
|
||||
.recall-item-content {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-primary);
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.welcome-banner {
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user