fix: 修复类型检查报错并统一有效时间比显示

This commit is contained in:
2026-09-13 23:38:49 +08:00
parent 132cb706d7
commit 99a1b16196
7 changed files with 50 additions and 22 deletions
+4 -3
View File
@@ -168,7 +168,8 @@ lpt-fe/src/
### 提交前检查 ### 提交前检查
- 运行 `npx vue-tsc --noEmit``npm run lint`0 error 才可提交;`no-explicit-any` 允许存量警告,新代码避免 any)、`npm run test``npm run build` - 运行 `npm run type-check``npm run lint`0 error 才可提交;`no-explicit-any` 允许存量警告,新代码避免 any)、`npm run test``npm run build`
- **不要用 `npx vue-tsc --noEmit` 作为检查手段**`tsconfig.json` 是 solution 风格(`files: []` + 仅 `references`),该命令会解析到根配置而检查 0 个源文件,永远假绿。Docker 构建跑的是 `npm run type-check``vue-tsc --build --force`,走 project references),只有它才真正校验。
- 提交信息按 Git 提交规范使用中文短句;样式类改动用 `style:`,清理类用 `refactor:` / `chore:` - 提交信息按 Git 提交规范使用中文短句;样式类改动用 `style:`,清理类用 `refactor:` / `chore:`
## 开发配置 ## 开发配置
@@ -185,7 +186,7 @@ npm run dev
npm run build npm run build
npm run test npm run test
npm run test:e2e npm run test:e2e
npx vue-tsc --noEmit npm run type-check
npm run lint npm run lint
npm run test:coverage npm run test:coverage
``` ```
@@ -194,7 +195,7 @@ npm run test:coverage
```bash ```bash
npx vite build npx vite build
npx vue-tsc --noEmit npm run type-check
``` ```
## Docker ## Docker
@@ -113,6 +113,8 @@ describe('StartTask.vue', () => {
expect(wrapper.vm.taskInfo.sessionState).toBe('PAUSED') expect(wrapper.vm.taskInfo.sessionState).toBe('PAUSED')
expect(wrapper.vm.taskInfo.actualTime).toBe(10) expect(wrapper.vm.taskInfo.actualTime).toBe(10)
expect(wrapper.vm.taskInfo.effectiveTime).toBe(20) expect(wrapper.vm.taskInfo.effectiveTime).toBe(20)
// 后端返回 0.8,UI 统一按百分比展示
expect(wrapper.vm.taskInfo.effectivenessRatio).toBe('80.0%')
expect(wrapper.vm.timerRunning).toBe(false) expect(wrapper.vm.timerRunning).toBe(false)
wrapper.unmount() wrapper.unmount()
}) })
+22
View File
@@ -0,0 +1,22 @@
import { describe, it, expect } from 'vitest'
import { formatRatio } from '@/utils/format'
describe('formatRatio', () => {
it('把 0~1 的比率格式化为一位小数的百分比', () => {
expect(formatRatio(0.8)).toBe('80.0%')
expect(formatRatio(0.8571)).toBe('85.7%')
expect(formatRatio(1)).toBe('100.0%')
expect(formatRatio(0)).toBe('0.0%')
})
it('兼容后端返回字符串形式的比率', () => {
expect(formatRatio('0.8')).toBe('80.0%')
})
it('空值与非法值统一显示 "-"', () => {
expect(formatRatio()).toBe('-')
expect(formatRatio(null)).toBe('-')
expect(formatRatio('')).toBe('-')
expect(formatRatio('abc')).toBe('-')
})
})
+3 -13
View File
@@ -8,7 +8,9 @@ import {
type ReviewFeedItem, type ReviewFeedItem,
} from "@/api/review"; } from "@/api/review";
import { getSessionDetail } from "@/api/studySessions"; import { getSessionDetail } from "@/api/studySessions";
import type { SessionDetail } from "@/api/studySessions";
import { updateFragments } from "@/api/reportFragments"; import { updateFragments } from "@/api/reportFragments";
import { formatRatio } from "@/utils/format";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import MarkdownRenderer from "@/components/MarkdownRenderer.vue"; import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
@@ -42,14 +44,7 @@ const isEditing = ref(false);
const editContent = ref(""); const editContent = ref("");
const saving = ref(false); const saving = ref(false);
const sessionInfo = ref<{ const sessionInfo = ref<SessionDetail | null>(null);
taskName: string;
sessionNum: string;
actualTime?: number;
effectiveTime?: number;
effectivenessRatio?: number;
startTime?: string;
} | null>(null);
const taskSessions = ref<{ const taskSessions = ref<{
sessionNum: string; sessionNum: string;
@@ -68,11 +63,6 @@ const formatDuration = (seconds?: number): string => {
return `${s}`; return `${s}`;
}; };
const formatRatio = (ratio?: number): string => {
if (ratio == null) return "-";
return `${(ratio * 100).toFixed(1)}%`;
};
const loadTaskHistory = async (tn: string) => { const loadTaskHistory = async (tn: string) => {
try { try {
const res = await getTaskReview(tn); const res = await getTaskReview(tn);
+3 -2
View File
@@ -18,6 +18,7 @@ import { getFragmentsBySession, updateFragments } from "@/api/reportFragments";
import router from "@/router"; import router from "@/router";
import MarkdownRenderer from "@/components/MarkdownRenderer.vue"; import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
import { renderMarkdown } from "@/utils/markdown"; import { renderMarkdown } from "@/utils/markdown";
import { formatRatio } from "@/utils/format";
import { useSessionHistory } from "@/components/composables/useSessionHistory"; import { useSessionHistory } from "@/components/composables/useSessionHistory";
import { useSummaryReport } from "@/components/composables/useSummaryReport"; import { useSummaryReport } from "@/components/composables/useSummaryReport";
import { useSessionExpectation } from "@/components/composables/useSessionExpectation"; import { useSessionExpectation } from "@/components/composables/useSessionExpectation";
@@ -110,7 +111,7 @@ const taskInfo = ref({
lastStartTime: "", lastStartTime: "",
actualTime: 0, actualTime: 0,
effectiveTime: 0, effectiveTime: 0,
effectivenessRatio: "--", effectivenessRatio: "-",
pointerPosition: 0, pointerPosition: 0,
systemMessage: "", systemMessage: "",
}); });
@@ -306,7 +307,7 @@ const stopTimer = async () => {
taskInfo.value.sessionState = d.sessionState ?? "PAUSED"; taskInfo.value.sessionState = d.sessionState ?? "PAUSED";
taskInfo.value.actualTime = d.actualTime ?? 0; taskInfo.value.actualTime = d.actualTime ?? 0;
taskInfo.value.effectiveTime = d.effectiveTime ?? 0; taskInfo.value.effectiveTime = d.effectiveTime ?? 0;
taskInfo.value.effectivenessRatio = d.effectivenessRatio ?? 0; taskInfo.value.effectivenessRatio = formatRatio(d.effectivenessRatio);
taskInfo.value.startTime = d.startTime ?? taskInfo.value.startTime; taskInfo.value.startTime = d.startTime ?? taskInfo.value.startTime;
taskInfo.value.endTime = d.endTime ?? ""; taskInfo.value.endTime = d.endTime ?? "";
taskInfo.value.lastStartTime = d.lastStartTime ?? ""; taskInfo.value.lastStartTime = d.lastStartTime ?? "";
+6 -4
View File
@@ -235,7 +235,7 @@ const removeTask = async () => {
// ============ 优先级权重配置 ============ // ============ 优先级权重配置 ============
const weightsDialogVisible = ref(false); const weightsDialogVisible = ref(false);
const savingWeights = ref(false); const savingWeights = ref(false);
const weightItems = ref([ const weightItems = ref<{ key: keyof PriorityWeights; label: string; value: number }[]>([
{ key: "urgencyWeight", label: "紧急性", value: 35 }, { key: "urgencyWeight", label: "紧急性", value: 35 },
{ key: "importanceWeight", label: "重要性", value: 25 }, { key: "importanceWeight", label: "重要性", value: 25 },
{ key: "contentDifficultyWeight", label: "内容难度", value: 20 }, { key: "contentDifficultyWeight", label: "内容难度", value: 20 },
@@ -274,9 +274,11 @@ const saveWeights = async () => {
} }
savingWeights.value = true; savingWeights.value = true;
try { try {
const payload = Object.fromEntries( // weightItems 的 key 已由 keyof PriorityWeights 约束,循环后必然覆盖全部维度
weightItems.value.map((item) => [item.key, item.value / 100]) const payload = {} as PriorityWeights;
) as unknown as PriorityWeights; for (const item of weightItems.value) {
payload[item.key] = item.value / 100;
}
const res = await savePriorityWeights(payload); const res = await savePriorityWeights(payload);
if (res?.code === 200) { if (res?.code === 200) {
weightsDialogVisible.value = false; weightsDialogVisible.value = false;
+10
View File
@@ -0,0 +1,10 @@
/**
* 把 0~1 的比率格式化为百分比文案。
* 后端可能返回数字或字符串,空值与非法值统一显示 "-"。
*/
export const formatRatio = (ratio?: number | string | null): string => {
if (ratio == null || ratio === "") return "-";
const value = Number(ratio);
if (Number.isNaN(value)) return "-";
return `${(value * 100).toFixed(1)}%`;
};