重构(学习页): 前端仅展示并修复运行稳定性问题
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
<!-- src/views/StudyTimer.vue -->
|
<!-- src/views/StudyTimer.vue -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||||
import { useTimer } from '@/components/composables/useTimer';
|
import { useTimer } from '@/components/composables/useTimer';
|
||||||
import { continueSession, pauseSession, endSession, startOrContinueStudySession } from '@/api/studySessions';
|
import { continueSession, pauseSession, endSession, startOrContinueStudySession } from '@/api/studySessions';
|
||||||
import { useStudyFragment } from '@/components/composables/fragment';
|
import { useStudyFragment } from '@/components/composables/fragment';
|
||||||
import {routes} from "vue-router/auto-routes";
|
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -49,27 +48,6 @@ const {
|
|||||||
requestAudioPermission
|
requestAudioPermission
|
||||||
} = useTimer();
|
} = useTimer();
|
||||||
|
|
||||||
const globalTimer = ref(Date.now());
|
|
||||||
|
|
||||||
const currentCycle = computed(() =>
|
|
||||||
Math.floor(taskInfo.value.effectiveTime / 60 / 25) + 1
|
|
||||||
);
|
|
||||||
|
|
||||||
const elapsedTime = computed(() => {
|
|
||||||
if (!taskInfo.value.startTime || !timerRunning.value) return { hours: 0, minutes: 0, seconds: 0 };
|
|
||||||
const start = new Date(taskInfo.value.startTime).getTime();
|
|
||||||
const now = globalTimer.value;
|
|
||||||
const diff = Math.max(Math.floor((now - start) / 1000), 0);
|
|
||||||
return {
|
|
||||||
hours: Math.floor(diff / 3600),
|
|
||||||
minutes: Math.floor((diff % 3600) / 60),
|
|
||||||
seconds: diff % 60,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const formatTime = (t: { hours: number; minutes: number; seconds: number }) =>
|
|
||||||
`${t.hours}时${t.minutes}分${t.seconds}秒`;
|
|
||||||
|
|
||||||
const progress = computed(() =>
|
const progress = computed(() =>
|
||||||
((timerMinutes.value * 60 + timerSeconds.value) / (25 * 60)) * 100
|
((timerMinutes.value * 60 + timerSeconds.value) / (25 * 60)) * 100
|
||||||
);
|
);
|
||||||
@@ -152,24 +130,33 @@ const initPage = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await loadTaskSession();
|
await loadTaskSession();
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
globalTimer.value = Date.now();
|
|
||||||
}, 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initPage();
|
initPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clear();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="task-container">
|
<div class="task-container">
|
||||||
|
<el-alert
|
||||||
|
v-if="taskInfo.systemMessage"
|
||||||
|
:title="taskInfo.systemMessage"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
class="session-alert"
|
||||||
|
/>
|
||||||
<div class="task-info">
|
<div class="task-info">
|
||||||
<p>当前任务【{{ taskInfo.taskName }}】</p>
|
<p>当前任务【{{ taskInfo.taskName }}】</p>
|
||||||
<p>当前循环:{{ currentCycle }}</p>
|
<p>会话状态:{{ taskInfo.sessionState }}</p>
|
||||||
<p>有效时间:{{ taskInfo.effectiveTime / 60 }}分钟</p>
|
<p>实际用时:{{ taskInfo.actualTime }}</p>
|
||||||
<p>本次运行时间:{{ formatTime(elapsedTime) }}</p>
|
<p>有效学习时长:{{ taskInfo.effectiveTime }}</p>
|
||||||
|
<p>有效时间比:{{ taskInfo.effectivenessRatio }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="timer-section">
|
<div class="timer-section">
|
||||||
<el-progress type="circle" :percentage="progress" :width="200">
|
<el-progress type="circle" :percentage="progress" :width="200">
|
||||||
@@ -225,6 +212,10 @@ onMounted(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
}
|
}
|
||||||
|
.session-alert {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
.task-info {
|
.task-info {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
|||||||
+26
-11
@@ -15,8 +15,9 @@ const fetchTasks = () => {
|
|||||||
tasks.splice(0, tasks.length, ...res.data.records.map((record: any) => ({
|
tasks.splice(0, tasks.length, ...res.data.records.map((record: any) => ({
|
||||||
title: record.taskName,
|
title: record.taskName,
|
||||||
description: record.taskDescription || '',
|
description: record.taskDescription || '',
|
||||||
|
materialURL: record.materialURL || record.materialUrl || "",
|
||||||
lastLearningStatus: record.lastLearningStatus || '未开始',
|
lastLearningStatus: record.lastLearningStatus || '未开始',
|
||||||
priority: Math.round(record.taskPriority),
|
priority: Number.isFinite(record.taskPriority) ? Math.round(record.taskPriority) : "-",
|
||||||
taskNum: record.taskNum,
|
taskNum: record.taskNum,
|
||||||
taskId: record.id,
|
taskId: record.id,
|
||||||
})));
|
})));
|
||||||
@@ -25,8 +26,11 @@ const fetchTasks = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const startTask = () => {
|
const startTask = () => {
|
||||||
|
if (!selectedTask.value) {
|
||||||
|
ElMessage.warning("请先选择一个任务");
|
||||||
|
return;
|
||||||
|
}
|
||||||
router.push({name:'start-task',params:{taskNum:selectedTask.value.taskNum}});
|
router.push({name:'start-task',params:{taskNum:selectedTask.value.taskNum}});
|
||||||
console.log('任务已开始:', selectedTask.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addTask = () => {
|
const addTask = () => {
|
||||||
@@ -34,21 +38,28 @@ const addTask = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const changeTask = () =>{
|
const changeTask = () =>{
|
||||||
|
if (!selectedTask.value) {
|
||||||
|
ElMessage.warning("请先选择一个任务");
|
||||||
|
return;
|
||||||
|
}
|
||||||
router.push({name:"update-task",params:{"taskId":selectedTask.value.taskId}})
|
router.push({name:"update-task",params:{"taskId":selectedTask.value.taskId}})
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeTask = () => {
|
const removeTask = () => {
|
||||||
request.del(`/tasks/${selectedTask.value.taskId}`,{}).then(res=>{
|
if (!selectedTask.value) {
|
||||||
if (res.code == '200'){
|
ElMessage.warning("请先选择一个任务");
|
||||||
ElMessage.success(`任务${selectedTask.value.title}删除成功`)
|
return;
|
||||||
}
|
}
|
||||||
});
|
request.del(`/tasks/${selectedTask.value.taskId}`,{}).then(res=>{
|
||||||
|
if (res.code === 200){
|
||||||
|
ElMessage.success(`任务${selectedTask.value.title}删除成功`)
|
||||||
const index = tasks.indexOf(selectedTask.value);
|
const index = tasks.indexOf(selectedTask.value);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
tasks.splice(index, 1);
|
tasks.splice(index, 1);
|
||||||
selectedTask.value = tasks[0] || null;
|
selectedTask.value = tasks[0] || null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectTask = (task: any) => {
|
const selectTask = (task: any) => {
|
||||||
@@ -84,12 +95,16 @@ onMounted(() => {
|
|||||||
<el-card class="task-card">
|
<el-card class="task-card">
|
||||||
<div v-if="selectedTask">
|
<div v-if="selectedTask">
|
||||||
<h3>任务描述</h3>
|
<h3>任务描述</h3>
|
||||||
<el-input type="textarea" v-model="selectedTask.description" :rows="4" />
|
<el-input type="textarea" :model-value="selectedTask.description" :rows="4" readonly />
|
||||||
|
|
||||||
|
<h3 class="section-title">学习材料</h3>
|
||||||
|
<el-link v-if="selectedTask.materialURL" :href="selectedTask.materialURL" type="primary" target="_blank">
|
||||||
|
{{ selectedTask.materialURL }}
|
||||||
|
</el-link>
|
||||||
|
<span v-else>未填写材料地址</span>
|
||||||
|
|
||||||
<h3 class="section-title">任务优先级</h3>
|
<h3 class="section-title">任务优先级</h3>
|
||||||
<el-radio-group v-model="selectedTask.priority">
|
<el-tag type="info">综合优先级:{{ selectedTask.priority }}</el-tag>
|
||||||
<el-radio-button v-for="i in 6" :label="i - 1" :key="i">{{ i - 1 }}</el-radio-button>
|
|
||||||
</el-radio-group>
|
|
||||||
|
|
||||||
<h3 class="section-title">上次该任务学习情况</h3>
|
<h3 class="section-title">上次该任务学习情况</h3>
|
||||||
<el-descriptions border column="1">
|
<el-descriptions border column="1">
|
||||||
|
|||||||
+11
-2
@@ -11,10 +11,19 @@ const axiosInstance = axios.create({
|
|||||||
|
|
||||||
const handleError = (error: any) => {
|
const handleError = (error: any) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
||||||
|
// 业务异常由 validateResponse 负责提示,这里透传,避免被网络错误文案覆盖。
|
||||||
|
if (error && typeof error === "object" && "code" in error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
ElMessage.error(`请求失败,服务器返回: ${error.response.data}`);
|
const backendMessage = error.response?.data?.message || JSON.stringify(error.response?.data);
|
||||||
|
ElMessage.error(`请求失败,服务器返回: ${backendMessage}`);
|
||||||
} else if (error.request) {
|
} else if (error.request) {
|
||||||
ElMessage.error("请求未收到响应,请检查接口运行状态");
|
ElMessage.error("请求未收到响应,请检查接口运行状态");
|
||||||
|
} else {
|
||||||
|
ElMessage.error(error.message || "请求失败");
|
||||||
}
|
}
|
||||||
throw new Error("网络不通畅,请检查您的网络连接或服务器状态");
|
throw new Error("网络不通畅,请检查您的网络连接或服务器状态");
|
||||||
};
|
};
|
||||||
@@ -61,7 +70,7 @@ const put = (url: string, data: any, config: any = {}) => {
|
|||||||
.catch(handleError);
|
.catch(handleError);
|
||||||
};
|
};
|
||||||
|
|
||||||
const del = (url: string, params: {}, config: any = {}) => {
|
const del = (url: string, params: Record<string, any> = {}, config: any = {}) => {
|
||||||
console.log("开始DELETE请求", basic_url + url, "参数:", params);
|
console.log("开始DELETE请求", basic_url + url, "参数:", params);
|
||||||
return axiosInstance.delete(url, { params, ...config })
|
return axiosInstance.delete(url, { params, ...config })
|
||||||
.then(validateResponse)
|
.then(validateResponse)
|
||||||
|
|||||||
Reference in New Issue
Block a user