feat(复习):回忆复习页面与入口
- 新增 ReviewRecall.vue 回忆复习页面(双栏布局) - 左栏:回忆大纲输入区、对比结果展示(✅❌着色树) - 右栏:标准导图(默认折叠)、编辑、重新生成 - 遗漏项列表,可点击回看原文 - 回忆历史记录查询 - api/standardMindMap.ts:标准导图与回忆对比 API - router:新增 /review/recall/:taskNum 路由 - Review.vue 行操作中增加回忆复习按钮 - ReviewDetail.vue 思维导图区增加回忆复习入口
This commit is contained in:
@@ -4,6 +4,11 @@ import request from "@/utils/request";
|
||||
import router from "@/router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { getReviewTaskStatsByTask } from "@/api/review";
|
||||
import {
|
||||
getTaskApplications,
|
||||
updateTaskApplication,
|
||||
type TaskApplication,
|
||||
} from "@/api/tasks";
|
||||
|
||||
interface TaskItem {
|
||||
title: string;
|
||||
@@ -27,6 +32,14 @@ interface TaskItem {
|
||||
const tasks = ref<TaskItem[]>([]);
|
||||
const selectedTaskId = ref<number | null>(null);
|
||||
const loading = ref(false);
|
||||
const taskApplications = ref<TaskApplication[]>([]);
|
||||
const applicationsLoading = ref(false);
|
||||
|
||||
const applicationStatusOptions: { label: string; value: TaskApplication["status"] }[] = [
|
||||
{ label: "待应用", value: "TODO" },
|
||||
{ label: "进行中", value: "DOING" },
|
||||
{ label: "已完成", value: "DONE" },
|
||||
];
|
||||
|
||||
const selectedTask = computed(() =>
|
||||
tasks.value.find((item) => item.taskId === selectedTaskId.value) || null
|
||||
@@ -62,6 +75,36 @@ async function loadTaskStats(taskNum: string) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTaskApplications(taskNum: string) {
|
||||
applicationsLoading.value = true;
|
||||
try {
|
||||
const res = await getTaskApplications(taskNum);
|
||||
taskApplications.value = res?.data || [];
|
||||
} catch (error: any) {
|
||||
taskApplications.value = [];
|
||||
ElMessage.error(error?.message || "应用场景加载失败");
|
||||
} finally {
|
||||
applicationsLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const changeApplicationStatus = async (item: TaskApplication) => {
|
||||
try {
|
||||
await updateTaskApplication(item.id, {
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
resourceUrl: item.resourceUrl,
|
||||
status: item.status,
|
||||
});
|
||||
ElMessage.success("应用场景状态已更新");
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || "状态更新失败");
|
||||
if (selectedTask.value) {
|
||||
await loadTaskApplications(selectedTask.value.taskNum);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const fetchTasks = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
@@ -91,10 +134,12 @@ const fetchTasks = async () => {
|
||||
selectedTaskId.value = nextSelectedTaskId;
|
||||
if (targetTask && shouldLoadStats) {
|
||||
await loadTaskStats(targetTask.taskNum);
|
||||
await loadTaskApplications(targetTask.taskNum);
|
||||
}
|
||||
} catch (error: any) {
|
||||
tasks.value = [];
|
||||
selectedTaskId.value = null;
|
||||
taskApplications.value = [];
|
||||
ElMessage.error(error?.message || "任务列表加载失败,请重试");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
@@ -105,6 +150,9 @@ watch(selectedTaskId, (newId) => {
|
||||
const task = tasks.value.find(t => t.taskId === newId);
|
||||
if (task) {
|
||||
loadTaskStats(task.taskNum);
|
||||
loadTaskApplications(task.taskNum);
|
||||
} else {
|
||||
taskApplications.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -229,6 +277,39 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-block" v-loading="applicationsLoading">
|
||||
<p class="label">应用场景</p>
|
||||
<div v-if="taskApplications.length > 0" class="application-list">
|
||||
<div
|
||||
v-for="item in taskApplications"
|
||||
:key="item.id"
|
||||
class="application-item"
|
||||
>
|
||||
<div class="application-main">
|
||||
<strong>{{ item.title }}</strong>
|
||||
<el-select
|
||||
v-model="item.status"
|
||||
size="small"
|
||||
class="application-status"
|
||||
@change="changeApplicationStatus(item)"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in applicationStatusOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<p v-if="item.description">{{ item.description }}</p>
|
||||
<el-link v-if="item.resourceUrl" :href="item.resourceUrl" target="_blank" type="primary">
|
||||
{{ item.resourceUrl }}
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="empty-text">暂未设置应用场景</p>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty v-else description="暂无任务,请先创建任务" />
|
||||
</article>
|
||||
@@ -365,6 +446,51 @@ onMounted(() => {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.application-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.application-item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
}
|
||||
|
||||
.application-item:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.application-item:last-child {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.application-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.application-main strong {
|
||||
min-width: 0;
|
||||
color: var(--text-primary);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.application-status {
|
||||
width: 112px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.application-item p {
|
||||
margin: 8px 0 0;
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.action-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
@@ -404,5 +530,14 @@ onMounted(() => {
|
||||
.action-card {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.application-main {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.application-status {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user