feat(会话):历史学习记录折叠面板(残片/报告分页+搜索)
StartTask.vue 新增「📚 历史学习记录」折叠区域:
- 默认折叠,点击标题展开
- 支持残片/报告两个 tab 切换
- 搜索框带 300ms 防抖,对内容 LIKE 搜索
- 分页每页 10 条
- 每条记录前标注所属 sessionNum,与当前会话内容明确区分
- 展示 v-loading 和 el-empty 两种状态
后端新增两个分页接口(上一 commit):
- GET /study-sessions/tasks/{taskNum}/fragments
- GET /study-sessions/tasks/{taskNum}/reports
This commit is contained in:
@@ -36,3 +36,17 @@ export const upsertExpectation = (sessionNum: string, description: string) => {
|
||||
export const getReportDraft = (sessionNum: string) => {
|
||||
return request.get(`/study-sessions/${sessionNum}/report-draft`);
|
||||
};
|
||||
|
||||
/** 分页查询任务的历史残片 */
|
||||
export const getTaskFragments = (taskNum: string, page: number, size: number, keyword?: string) => {
|
||||
const params: Record<string, any> = { page, size };
|
||||
if (keyword) params.keyword = keyword;
|
||||
return request.get(`/study-sessions/tasks/${taskNum}/fragments`, params);
|
||||
};
|
||||
|
||||
/** 分页查询任务的历史报告 */
|
||||
export const getTaskReports = (taskNum: string, page: number, size: number, keyword?: string) => {
|
||||
const params: Record<string, any> = { page, size };
|
||||
if (keyword) params.keyword = keyword;
|
||||
return request.get(`/study-sessions/tasks/${taskNum}/reports`, params);
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from "@/api/studySessions";
|
||||
import { useStudyFragment } from "@/components/composables/fragment";
|
||||
import { getFragmentsBySession, updateFragments } from "@/api/reportFragments";
|
||||
import { getExpectation, getReportDraft, upsertExpectation } from "@/api/studySessions";
|
||||
import { getExpectation, getReportDraft, getTaskFragments, getTaskReports, upsertExpectation } from "@/api/studySessions";
|
||||
import router from "@/router";
|
||||
|
||||
const {
|
||||
@@ -44,6 +44,77 @@ const editingFragmentId = ref<number | null>(null);
|
||||
const editFragmentContent = ref("");
|
||||
const savingFragment = ref(false);
|
||||
|
||||
// 历史学习记录
|
||||
const showHistory = ref(false);
|
||||
const historyTab = ref("fragments");
|
||||
const historyKeyword = ref("");
|
||||
let historyDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
const loadingHistory = ref(false);
|
||||
const historyFragments = ref<any[]>([]);
|
||||
const fragmentsTotal = ref(0);
|
||||
const fragmentsPage = ref(1);
|
||||
const historyReports = ref<any[]>([]);
|
||||
const reportsTotal = ref(0);
|
||||
const reportsPage = ref(1);
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
const loadHistoryFragments = async () => {
|
||||
loadingHistory.value = true;
|
||||
try {
|
||||
const res = await getTaskFragments(taskNum, fragmentsPage.value, PAGE_SIZE, historyKeyword.value || undefined);
|
||||
const data = res?.data;
|
||||
historyFragments.value = data?.records || [];
|
||||
fragmentsTotal.value = data?.total || 0;
|
||||
} catch {
|
||||
historyFragments.value = [];
|
||||
fragmentsTotal.value = 0;
|
||||
} finally {
|
||||
loadingHistory.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadHistoryReports = async () => {
|
||||
loadingHistory.value = true;
|
||||
try {
|
||||
const res = await getTaskReports(taskNum, reportsPage.value, PAGE_SIZE, historyKeyword.value || undefined);
|
||||
const data = res?.data;
|
||||
historyReports.value = data?.records || [];
|
||||
reportsTotal.value = data?.total || 0;
|
||||
} catch {
|
||||
historyReports.value = [];
|
||||
reportsTotal.value = 0;
|
||||
} finally {
|
||||
loadingHistory.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const searchHistory = () => {
|
||||
if (historyDebounce) clearTimeout(historyDebounce);
|
||||
historyDebounce = setTimeout(() => {
|
||||
fragmentsPage.value = 1;
|
||||
reportsPage.value = 1;
|
||||
if (historyTab.value === "fragments") loadHistoryFragments();
|
||||
else loadHistoryReports();
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const onHistoryTabChange = () => {
|
||||
fragmentsPage.value = 1;
|
||||
reportsPage.value = 1;
|
||||
if (historyTab.value === "fragments") loadHistoryFragments();
|
||||
else loadHistoryReports();
|
||||
};
|
||||
|
||||
const onFragmentsPageChange = (p: number) => {
|
||||
fragmentsPage.value = p;
|
||||
loadHistoryFragments();
|
||||
};
|
||||
|
||||
const onReportsPageChange = (p: number) => {
|
||||
reportsPage.value = p;
|
||||
loadHistoryReports();
|
||||
};
|
||||
|
||||
const route = useRoute();
|
||||
const taskNum = route.params.taskNum as string;
|
||||
|
||||
@@ -552,6 +623,41 @@ onUnmounted(() => {
|
||||
</template>
|
||||
</div>
|
||||
</article>
|
||||
<!-- 历史学习记录 -->
|
||||
<article class="surface-card history-card">
|
||||
<div class="history-header" @click="showHistory = !showHistory">
|
||||
<div class="history-header-left">
|
||||
<span class="history-toggle">{{ showHistory ? "▾" : "▸" }}</span>
|
||||
<h3>📚 历史学习记录</h3>
|
||||
</div>
|
||||
<el-tag size="small" type="info" effect="plain">展开后查看该任务全部残片与报告</el-tag>
|
||||
</div>
|
||||
<template v-if="showHistory">
|
||||
<el-input v-model="historyKeyword" placeholder="搜索历史内容…" clearable class="history-search" @input="searchHistory" @clear="searchHistory" />
|
||||
<el-tabs v-model="historyTab" @tab-change="onHistoryTabChange" class="history-tabs">
|
||||
<el-tab-pane label="学习残片" name="fragments">
|
||||
<div v-loading="loadingHistory" class="history-list">
|
||||
<div v-for="item in historyFragments" :key="item.id" class="history-item">
|
||||
<span class="session-tag">{{ item.sessionNum }}</span>
|
||||
<span class="history-content">{{ item.content }}</span>
|
||||
</div>
|
||||
<el-empty v-if="!loadingHistory && historyFragments.length === 0" description="暂未找到匹配的残片" :image-size="48" />
|
||||
</div>
|
||||
<el-pagination v-if="fragmentsTotal > 10" small layout="prev, pager, next" :total="fragmentsTotal" :page-size="10" :current-page="fragmentsPage" @current-change="onFragmentsPageChange" background />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="学习报告" name="reports">
|
||||
<div v-loading="loadingHistory" class="history-list">
|
||||
<div v-for="item in historyReports" :key="item.id" class="history-item report-item">
|
||||
<span class="session-tag">{{ item.sessionNum }}</span>
|
||||
<p class="history-content">{{ item.content }}</p>
|
||||
</div>
|
||||
<el-empty v-if="!loadingHistory && historyReports.length === 0" description="暂未找到匹配的报告" :image-size="48" />
|
||||
</div>
|
||||
<el-pagination v-if="reportsTotal > 10" small layout="prev, pager, next" :total="reportsTotal" :page-size="10" :current-page="reportsPage" @current-change="onReportsPageChange" background />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<el-dialog v-model="fragmentsDialogVisible" title="生成学习残片" width="520px">
|
||||
|
||||
Reference in New Issue
Block a user