feat(review): 实现复习内容滚动展示与交互

- Welcome.vue 新增复习内容水平滚动条,展示报告/残片摘要
- 新增 ReviewDetail.vue 复习详情页,展示完整内容及会话上下文
- 新增 review.ts API 模块,路由注册 /review/detail/:type/:id
This commit is contained in:
2026-05-23 19:22:55 +08:00
parent 3a3a96da3c
commit 52045ecbeb
5 changed files with 416 additions and 0 deletions
+136
View File
@@ -1,5 +1,37 @@
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import router from "@/router";
import { getReviewFeed, type ReviewFeedItem } from "@/api/review";
const reviewItems = ref<ReviewFeedItem[]>([]);
const isHovering = ref(false);
const truncatedItems = computed(() =>
reviewItems.value.map((item) => ({
...item,
display:
item.content.length > 50
? item.content.substring(0, 50) + "..."
: item.content,
}))
);
// 复制一份实现无缝循环
const duplicatedItems = computed(() => [...truncatedItems.value, ...truncatedItems.value]);
const loadReviewFeed = async () => {
try {
const res = await getReviewFeed(30);
reviewItems.value = res?.data || [];
} catch {
// feed 加载失败不影响页面主体功能
}
};
const goToReviewDetail = (item: ReviewFeedItem) => {
const type = item.sourceType === "REPORT" ? "report" : "fragment";
router.push(`/review/detail/${type}/${item.id}`);
};
const startStudy = () => {
router.push("/study");
@@ -8,6 +40,10 @@ const startStudy = () => {
const startReview = () => {
router.push("/review");
};
onMounted(() => {
loadReviewFeed();
});
</script>
<template>
@@ -36,6 +72,36 @@ const startReview = () => {
</article>
</div>
<div class="review-scroll-section surface-card" v-if="reviewItems.length > 0">
<h3>复习滚动条</h3>
<div class="review-scroll-track">
<div
class="review-scroll-content"
:class="{ paused: isHovering }"
@mouseenter="isHovering = true"
@mouseleave="isHovering = false"
>
<span
v-for="(item, index) in duplicatedItems"
:key="index"
class="review-chip"
@click="goToReviewDetail(item)"
>
<el-tag
size="small"
:type="item.sourceType === 'REPORT' ? 'success' : 'warning'"
effect="plain"
disable-transitions
>
{{ item.sourceType === 'REPORT' ? '报告' : '残片' }}
</el-tag>
<strong>{{ item.taskName }}</strong>
<span>{{ item.display }}</span>
</span>
</div>
</div>
</div>
<div class="tips-board surface-card">
<h3>建议流程</h3>
<ol>
@@ -90,6 +156,76 @@ const startReview = () => {
width: 100%;
}
/* 复习滚动条区域 */
.review-scroll-section {
padding: 20px 22px;
overflow: hidden;
}
.review-scroll-section h3 {
margin: 0 0 12px;
color: var(--green-900);
font-size: 16px;
}
.review-scroll-track {
overflow: hidden;
mask-image: linear-gradient(to right, transparent, black 3%, black 97%, transparent);
-webkit-mask-image: linear-gradient(to right, transparent, black 3%, black 97%, transparent);
}
.review-scroll-content {
display: inline-flex;
gap: 14px;
white-space: nowrap;
animation: review-ticker 80s linear infinite;
}
.review-scroll-content.paused {
animation-play-state: paused;
}
@keyframes review-ticker {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
.review-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 14px;
background: var(--surface-strong);
border: 1px solid var(--border-soft);
border-radius: 24px;
cursor: pointer;
transition: background 0.2s, box-shadow 0.2s;
flex-shrink: 0;
user-select: none;
}
.review-chip:hover {
background: #e8f5e9;
box-shadow: var(--shadow-soft);
}
.review-chip strong {
color: var(--green-700);
font-size: 13px;
}
.review-chip span {
color: var(--text-secondary);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
max-width: 220px;
}
.tips-board {
padding: 20px 22px;
}