feat: 学习会话页展示当前会话碎片列表并支持编辑
- 会话加载时获取碎片列表,创建碎片后自动刷新 - 碎片列表支持内联编辑(textarea + 保存/取消) - 修复多根节点问题,用 div 包裹模板内容以兼容 Transition 组件
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useTimer } from "@/components/composables/useTimer";
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
startOrContinueStudySession,
|
||||
} from "@/api/studySessions";
|
||||
import { useStudyFragment } from "@/components/composables/fragment";
|
||||
import { getFragmentsBySession, updateFragments } from "@/api/reportFragments";
|
||||
import router from "@/router";
|
||||
|
||||
const {
|
||||
@@ -23,6 +24,16 @@ const {
|
||||
const summaryDialogVisible = ref(false);
|
||||
const summaryContent = ref("");
|
||||
|
||||
// 当前会话碎片列表
|
||||
interface FragmentItem {
|
||||
id: number;
|
||||
content: string;
|
||||
}
|
||||
const fragmentsList = ref<FragmentItem[]>([]);
|
||||
const editingFragmentId = ref<number | null>(null);
|
||||
const editFragmentContent = ref("");
|
||||
const savingFragment = ref(false);
|
||||
|
||||
const route = useRoute();
|
||||
const taskNum = route.params.taskNum as string;
|
||||
|
||||
@@ -213,6 +224,7 @@ const loadTaskSession = async () => {
|
||||
} else {
|
||||
syncDisplay(taskInfo.value.pointerPosition || 25 * 60 * 1000);
|
||||
}
|
||||
loadFragments();
|
||||
} catch (err: any) {
|
||||
ElMessage.error(err.message || "加载任务失败");
|
||||
}
|
||||
@@ -227,6 +239,53 @@ const initPage = async () => {
|
||||
await loadTaskSession();
|
||||
};
|
||||
|
||||
// 碎片列表
|
||||
const loadFragments = async () => {
|
||||
if (!taskInfo.value.sessionNum) return;
|
||||
try {
|
||||
const res = await getFragmentsBySession(taskInfo.value.sessionNum);
|
||||
if (res?.code === 200) {
|
||||
fragmentsList.value = res.data || [];
|
||||
}
|
||||
} catch {
|
||||
// 非关键数据
|
||||
}
|
||||
};
|
||||
|
||||
const startEditFragment = (fragment: FragmentItem) => {
|
||||
editingFragmentId.value = fragment.id;
|
||||
editFragmentContent.value = fragment.content;
|
||||
};
|
||||
|
||||
const cancelEditFragment = () => {
|
||||
editingFragmentId.value = null;
|
||||
editFragmentContent.value = "";
|
||||
};
|
||||
|
||||
const saveEditFragment = async (id: number) => {
|
||||
if (!editFragmentContent.value.trim()) {
|
||||
ElMessage.warning("内容不能为空");
|
||||
return;
|
||||
}
|
||||
savingFragment.value = true;
|
||||
try {
|
||||
const res = await updateFragments(id, editFragmentContent.value);
|
||||
if (res?.code === 200) {
|
||||
const item = fragmentsList.value.find((f) => f.id === id);
|
||||
if (item) item.content = editFragmentContent.value;
|
||||
editingFragmentId.value = null;
|
||||
editFragmentContent.value = "";
|
||||
ElMessage.success("保存成功");
|
||||
} else {
|
||||
ElMessage.error(res?.message || "保存失败");
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message || "请求失败");
|
||||
} finally {
|
||||
savingFragment.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
displayInterval = window.setInterval(() => {
|
||||
nowTimestamp.value = Date.now();
|
||||
@@ -234,6 +293,13 @@ onMounted(() => {
|
||||
initPage();
|
||||
});
|
||||
|
||||
// 碎片创建成功后刷新列表
|
||||
watch(fragmentsDialogVisible, (newVal, oldVal) => {
|
||||
if (oldVal === true && newVal === false) {
|
||||
loadFragments();
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (displayInterval) {
|
||||
clearInterval(displayInterval);
|
||||
@@ -243,6 +309,7 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<section class="start-page">
|
||||
<div class="task-info-bar">
|
||||
<el-tag type="success" effect="plain">任务编号:{{ taskInfo.taskNum }}</el-tag>
|
||||
@@ -298,6 +365,33 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<!-- 当前会话学习残片 -->
|
||||
<article class="surface-card fragments-card" v-if="fragmentsList.length > 0">
|
||||
<h3>当前会话学习残片</h3>
|
||||
<div
|
||||
v-for="fragment in fragmentsList"
|
||||
:key="fragment.id"
|
||||
class="fragment-item"
|
||||
>
|
||||
<template v-if="editingFragmentId === fragment.id">
|
||||
<el-input
|
||||
v-model="editFragmentContent"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入学习内容"
|
||||
/>
|
||||
<div class="fragment-edit-actions">
|
||||
<el-button size="small" @click="cancelEditFragment">取消</el-button>
|
||||
<el-button size="small" type="primary" :loading="savingFragment" @click="saveEditFragment(fragment.id)">保存</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="fragment-content">{{ fragment.content }}</span>
|
||||
<el-button size="small" @click="startEditFragment(fragment)">编辑</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<el-dialog v-model="fragmentsDialogVisible" title="生成学习残片" width="520px">
|
||||
@@ -325,6 +419,7 @@ onUnmounted(() => {
|
||||
<el-button type="success" @click="endTimer(summaryContent)">确认结束</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -425,6 +520,42 @@ onUnmounted(() => {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fragments-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.fragments-card h3 {
|
||||
margin: 0 0 14px;
|
||||
color: var(--green-900);
|
||||
}
|
||||
|
||||
.fragment-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
}
|
||||
|
||||
.fragment-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.fragment-content {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-primary);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.fragment-edit-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.session-grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
Reference in New Issue
Block a user