feat: 学习页展示任务学习统计
This commit is contained in:
+125
-2
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import request from "@/utils/request";
|
||||
import router from "@/router";
|
||||
import { ElMessage } from "element-plus";
|
||||
@@ -12,6 +12,15 @@ interface TaskItem {
|
||||
priority: number | string;
|
||||
taskNum: string;
|
||||
taskId: number;
|
||||
reportCount: number;
|
||||
fragmentCount: number;
|
||||
effectiveTime: number;
|
||||
statsLoaded: boolean;
|
||||
sessionCount: number;
|
||||
todayEffectiveTime: number;
|
||||
weekEffectiveTime: number;
|
||||
avgEffectiveTime: number;
|
||||
avgEffectivenessRatio: number;
|
||||
}
|
||||
|
||||
const tasks = ref<TaskItem[]>([]);
|
||||
@@ -22,9 +31,40 @@ const selectedTask = computed(() =>
|
||||
tasks.value.find((item) => item.taskId === selectedTaskId.value) || null
|
||||
);
|
||||
|
||||
const formatEffectiveTime = (seconds: number): string => {
|
||||
if (!seconds || seconds <= 0) return "0分钟";
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
if (hours > 0) return `${hours}小时${minutes}分钟`;
|
||||
return `${minutes}分钟`;
|
||||
};
|
||||
|
||||
async function loadTaskStats(taskNum: string) {
|
||||
const task = tasks.value.find(t => t.taskNum === taskNum);
|
||||
if (!task || task.statsLoaded) return;
|
||||
|
||||
try {
|
||||
const res = await request.get(`/review/tasks/${taskNum}`);
|
||||
if (res?.data) {
|
||||
task.reportCount = res.data.reportCount ?? 0;
|
||||
task.fragmentCount = res.data.fragmentCount ?? 0;
|
||||
task.effectiveTime = res.data.effectiveTime ?? 0;
|
||||
task.sessionCount = res.data.sessionCount ?? 0;
|
||||
task.todayEffectiveTime = res.data.todayEffectiveTime ?? 0;
|
||||
task.weekEffectiveTime = res.data.weekEffectiveTime ?? 0;
|
||||
task.avgEffectiveTime = res.data.avgEffectiveTime ?? 0;
|
||||
task.avgEffectivenessRatio = res.data.avgEffectivenessRatio ?? 0;
|
||||
task.statsLoaded = true;
|
||||
}
|
||||
} catch {
|
||||
// 静默处理,不影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
const fetchTasks = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const previousSelectedTaskId = selectedTaskId.value;
|
||||
const res = await request.get("/tasks", { pageSize: 100, pageNum: 1 });
|
||||
tasks.value = (res?.data?.records || []).map((record: any) => ({
|
||||
title: record.taskName,
|
||||
@@ -34,8 +74,23 @@ const fetchTasks = async () => {
|
||||
priority: Number.isFinite(record.taskPriority) ? Math.round(record.taskPriority) : "-",
|
||||
taskNum: record.taskNum,
|
||||
taskId: record.id,
|
||||
reportCount: 0,
|
||||
fragmentCount: 0,
|
||||
effectiveTime: 0,
|
||||
statsLoaded: false,
|
||||
sessionCount: 0,
|
||||
todayEffectiveTime: 0,
|
||||
weekEffectiveTime: 0,
|
||||
avgEffectiveTime: 0,
|
||||
avgEffectivenessRatio: 0,
|
||||
}));
|
||||
selectedTaskId.value = tasks.value[0]?.taskId ?? null;
|
||||
const targetTask = tasks.value.find(item => item.taskId === previousSelectedTaskId) || tasks.value[0];
|
||||
const nextSelectedTaskId = targetTask?.taskId ?? null;
|
||||
const shouldLoadStats = nextSelectedTaskId === selectedTaskId.value;
|
||||
selectedTaskId.value = nextSelectedTaskId;
|
||||
if (targetTask && shouldLoadStats) {
|
||||
await loadTaskStats(targetTask.taskNum);
|
||||
}
|
||||
} catch (error: any) {
|
||||
tasks.value = [];
|
||||
selectedTaskId.value = null;
|
||||
@@ -45,6 +100,13 @@ const fetchTasks = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
watch(selectedTaskId, (newId) => {
|
||||
const task = tasks.value.find(t => t.taskId === newId);
|
||||
if (task) {
|
||||
loadTaskStats(task.taskNum);
|
||||
}
|
||||
});
|
||||
|
||||
const startTask = () => {
|
||||
if (!selectedTask.value) {
|
||||
ElMessage.warning("请先选择一个任务");
|
||||
@@ -128,6 +190,44 @@ onMounted(() => {
|
||||
</el-link>
|
||||
<p v-else class="empty-text">暂未填写材料地址</p>
|
||||
</div>
|
||||
|
||||
<div class="detail-block" v-if="selectedTask.statsLoaded">
|
||||
<p class="label">学习统计</p>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">学习次数</span>
|
||||
<span class="stat-value">{{ selectedTask.sessionCount }} 次</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">今日有效学习</span>
|
||||
<span class="stat-value">{{ formatEffectiveTime(selectedTask.todayEffectiveTime) }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">本周有效学习</span>
|
||||
<span class="stat-value">{{ formatEffectiveTime(selectedTask.weekEffectiveTime) }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">累计有效学习</span>
|
||||
<span class="stat-value">{{ formatEffectiveTime(selectedTask.effectiveTime) }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">平均学习时长</span>
|
||||
<span class="stat-value">{{ formatEffectiveTime(selectedTask.avgEffectiveTime) }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">平均有效时间比</span>
|
||||
<span class="stat-value">{{ (selectedTask.avgEffectivenessRatio * 100).toFixed(1) }}%</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">学习报告</span>
|
||||
<span class="stat-value">{{ selectedTask.reportCount }} 篇</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">学习残片</span>
|
||||
<span class="stat-value">{{ selectedTask.fragmentCount }} 条</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty v-else description="暂无任务,请先创建任务" />
|
||||
</article>
|
||||
@@ -241,6 +341,29 @@ onMounted(() => {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.action-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user