diff --git a/apps/web/components/dev-task/DevTaskDetailDrawer.tsx b/apps/web/components/dev-task/DevTaskDetailDrawer.tsx
index b2f9cb1..e9fc04e 100644
--- a/apps/web/components/dev-task/DevTaskDetailDrawer.tsx
+++ b/apps/web/components/dev-task/DevTaskDetailDrawer.tsx
@@ -161,7 +161,7 @@ export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose }: Props) {
开始开发
-
{task.startDate}
+
{task.startDate.slice(0, 16).replace('T', ' ')}
)}
{task.dueDate && (
@@ -175,7 +175,7 @@ export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose }: Props) {
提测
- {task.completedAt}
+ {task.completedAt.slice(0, 16).replace('T', ' ')}
)}
diff --git a/apps/web/components/test-case/TestCaseDetailDrawer.tsx b/apps/web/components/test-case/TestCaseDetailDrawer.tsx
index 5d87e70..8dd9cfc 100644
--- a/apps/web/components/test-case/TestCaseDetailDrawer.tsx
+++ b/apps/web/components/test-case/TestCaseDetailDrawer.tsx
@@ -127,8 +127,8 @@ export function TestCaseDetailDrawer({ testCaseId, onClose, onCreateBug }: Props
负责人:{tc.assigneeId || '-'}
创建人:{tc.createdBy}
创建日期:{tc.createdAt.slice(0, 10)}
- {tc.startedAt && 开始执行:{tc.startedAt}
}
- {tc.completedAt && 执行完成:{tc.completedAt}
}
+ {tc.startedAt && 开始执行:{tc.startedAt.slice(0, 16).replace('T', ' ')}
}
+ {tc.completedAt && 执行完成:{tc.completedAt.slice(0, 16).replace('T', ' ')}
}
{tc.startedAt && 测试耗时:{calcActualHoursByDates(tc.startedAt, tc.completedAt)}h
}
diff --git a/apps/web/lib/dev-task.ts b/apps/web/lib/dev-task.ts
index 3881f9a..56757c4 100644
--- a/apps/web/lib/dev-task.ts
+++ b/apps/web/lib/dev-task.ts
@@ -83,18 +83,12 @@ export function formatHours(hours: number): string {
export function calcActualHoursByDates(startDate?: string, completedAt?: string): number {
if (!startDate) return 0;
- const end = completedAt || new Date().toISOString().slice(0, 10);
- const start = new Date(startDate);
- const endDate = new Date(end);
- if (isNaN(start.getTime()) || isNaN(endDate.getTime()) || endDate < start) return 0;
- let workDays = 0;
- const current = new Date(start);
- while (current <= endDate) {
- const day = current.getDay();
- if (day !== 0 && day !== 6) workDays++;
- current.setDate(current.getDate() + 1);
- }
- return workDays * 8;
+ const end = completedAt || new Date().toISOString();
+ const startMs = new Date(startDate).getTime();
+ const endMs = new Date(end).getTime();
+ if (isNaN(startMs) || isNaN(endMs) || endMs < startMs) return 0;
+ const diffHours = (endMs - startMs) / (1000 * 60 * 60);
+ return Math.round(diffHours * 2) / 2; // 精确到0.5h
}
export function generateTaskNo(existingTasks: DevTask[]): string {
diff --git a/apps/web/stores/useDevTaskStore.ts b/apps/web/stores/useDevTaskStore.ts
index 0f8d6cc..e842b1e 100644
--- a/apps/web/stores/useDevTaskStore.ts
+++ b/apps/web/stores/useDevTaskStore.ts
@@ -77,10 +77,9 @@ export const useDevTaskStore = create((set, get) => ({
if (!canTransition(task.status, to)) {
return { ok: false, message: `不允许从「${task.status}」流转到「${to}」` };
}
- const today = new Date().toISOString().slice(0, 10);
- const startDate = (to === 'in_progress' && !task.startDate) ? today : task.startDate;
- // 开发完成时间 = 提测时间(不是测试完成时间)
- const completedAt = (to === 'submitted' && !task.completedAt) ? today : task.completedAt;
+ const now = new Date().toISOString();
+ const startDate = (to === 'in_progress' && !task.startDate) ? now : task.startDate;
+ const completedAt = (to === 'submitted' && !task.completedAt) ? now : task.completedAt;
get().updateTask(id, { status: to, startDate, completedAt });
return { ok: true };
},
diff --git a/apps/web/stores/useTestCaseStore.ts b/apps/web/stores/useTestCaseStore.ts
index 5fc7d65..0948528 100644
--- a/apps/web/stores/useTestCaseStore.ts
+++ b/apps/web/stores/useTestCaseStore.ts
@@ -74,14 +74,12 @@ export const useTestCaseStore = create((set, get) => ({
if (!canTcTransition(tc.status, to)) {
return { ok: false, message: `不允许从「${tc.status}」流转到「${to}」` };
}
- const today = new Date().toISOString().slice(0, 10);
+ const now = new Date().toISOString();
const patch: Partial = { status: to };
- // 开始执行时间
- if (to === 'running' && !tc.startedAt) patch.startedAt = today;
- // 执行完成时间(通过/失败/阻塞都算执行完成)
- if (to === 'passed' || to === 'failed' || to === 'blocked') patch.completedAt = today;
+ if (to === 'running' && !tc.startedAt) patch.startedAt = now;
+ if (to === 'passed' || to === 'failed' || to === 'blocked') patch.completedAt = now;
if (to === 'running' || to === 'passed' || to === 'failed' || to === 'blocked') {
- patch.executedAt = today;
+ patch.executedAt = now;
}
// 回退到执行中时清除完成时间
if (to === 'running') { patch.failReason = undefined; patch.blockReason = undefined; patch.completedAt = undefined; }