+
+
+
{{ taskInfo.taskName || "学习会话" }}
+
状态:{{ statusText }}
+
+ 任务编号:{{ taskInfo.taskNum }}
+
+
-
-
当前任务【{{ taskInfo.taskName }}】
-
会话状态:{{ taskInfo.sessionState }}
-
实际用时:{{ taskInfo.actualTime }}
-
有效学习时长:{{ taskInfo.effectiveTime }}
-
有效时间比:{{ taskInfo.effectivenessRatio }}
-
-
-
-
- {{ timerMinutes.toString().padStart(2, '0') }} :
- {{ timerSeconds.toString().padStart(2, '0') }}
+
+
+
+ 当前计时
+
+
+ {{ timerMinutes.toString().padStart(2, "0") }}:{{ timerSeconds.toString().padStart(2, "0") }}
+
+
+ 番茄钟默认 25 分钟,休息计时 5 分钟
+
+
+
+ 会话数据
+
+
+ 实际用时
+ {{ actualTimeText }}
+
+
+ 有效学习时长
+ {{ effectiveTimeText }}
+
+
+ 有效时间比
+ {{ taskInfo.effectivenessRatio }}
+
+
+ 会话状态
+ {{ statusText }}
+
-
+
+
+ 开始
+ 暂停
+ 生成残片
+ 休息
+ 结束会话
+
+
-
- 开始
- 暂停
- 生成学习残片
- 休息
- 结束
-
-
-
+
+
+
-
- 取消生成
- 确定生成
-
+ 取消
+ 确定生成
-
+
+
-
取消
- 确定
-
+ 确认结束
diff --git a/src/components/Study.vue b/src/components/Study.vue
index 001c4e4..ba17c3a 100644
--- a/src/components/Study.vue
+++ b/src/components/Study.vue
@@ -1,28 +1,48 @@
-
-
-
-
-
-
+
+
+
+
学习任务
+
先选任务,再确认材料与优先级,然后开始一次学习会话
+
+ 刷新列表
+
+
+
+
-
-
-
-
任务描述
-
+
+
+
+
+
{{ selectedTask.title }}
+ 优先级 {{ selectedTask.priority }}
+
- 学习材料
-
- {{ selectedTask.materialURL }}
-
- 未填写材料地址
+
- 任务优先级
- 综合优先级:{{ selectedTask.priority }}
+
+
学习材料
+
+ {{ selectedTask.materialURL }}
+
+
暂未填写材料地址
+
+
+
+
- 上次该任务学习情况
-
- 0.5
- 2小时
- 1小时
- 学习了“动态面板”的基本使用方式
-
-
-
-
-
-
-
- 开始任务
- 添加任务
- 更新任务
- 删除任务
- 刷新
-
-
-
+
+
+
+
diff --git a/src/components/composables/useTimer.ts b/src/components/composables/useTimer.ts
index 242acbb..cd13a1c 100644
--- a/src/components/composables/useTimer.ts
+++ b/src/components/composables/useTimer.ts
@@ -18,6 +18,14 @@ export function useTimer(audioUrl: string = '/resource/notification.mp3') {
timerRunning.value = false;
};
+ const syncDisplay = (durationMs: number) => {
+ const safeDuration = Math.max(durationMs, 0);
+ remainingTime.value = safeDuration;
+ timerMinutes.value = Math.floor(safeDuration / 1000 / 60);
+ timerSeconds.value = Math.floor((safeDuration / 1000) % 60);
+ timerIsOver.value = safeDuration === 0;
+ };
+
const handleCountdownComplete = () => {
audio.loop = true;
audio.play();
@@ -91,6 +99,7 @@ export function useTimer(audioUrl: string = '/resource/notification.mp3') {
timerRunning,
timerIsOver,
runCountdown,
+ syncDisplay,
clear,
checkAudioPermission,
requestAudioPermission,
diff --git a/src/router/index.ts b/src/router/index.ts
index 96415cb..5d86067 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,4 +1,5 @@
import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router";
+import StartTask from "@/components/StartTask.vue";
const routes: RouteRecordRaw[] = [
{
@@ -17,6 +18,7 @@ const routes: RouteRecordRaw[] = [
},
{
path: "/study",
+ name: "study",
component: () => import("@/components/Study.vue"),
meta: { requiresAuth: true },
},
@@ -28,7 +30,7 @@ const routes: RouteRecordRaw[] = [
{
path: "/start-task/:taskNum",
name: "start-task",
- component: () => import("@/components/StartTask.vue"),
+ component: StartTask,
meta: { requiresAuth: true },
},
{
@@ -58,6 +60,17 @@ const router = createRouter({
routes,
});
+router.onError((error, to) => {
+ const message = String((error as Error)?.message || "");
+ const isChunkLoadError =
+ message.includes("Failed to fetch dynamically imported module") ||
+ message.includes("Importing a module script failed") ||
+ message.includes("Loading chunk");
+ if (isChunkLoadError && typeof window !== "undefined") {
+ window.location.assign(to.fullPath);
+ }
+});
+
router.beforeEach((to) => {
if (to.meta.requiresAuth === false) return true;
const isLoggedIn = localStorage.getItem("isLoggedIn") === "true";