From 75b0d12fa07b37c3bce6a46f3f63b3281c801aaf Mon Sep 17 00:00:00 2001 From: Script Generator Date: Fri, 12 Jun 2026 09:31:30 +0800 Subject: [PATCH] =?UTF-8?q?refactor(dev-task):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=B7=A5=E6=97=B6=E8=AE=B0=E5=BD=95=EF=BC=8C=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E7=94=B1=E5=BC=80=E5=A7=8B/=E5=AE=8C=E6=88=90=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E8=87=AA=E5=8A=A8=E8=AE=A1=E7=AE=97=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E5=B7=A5=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 状态流转到"开发中"时自动记录 startDate - 实际工时 = startDate → completedAt 的工作日 × 8h - 详情抽屉移除工时记录面板,增加"开发开始日期"展示 - 列表行工时展示同步改为日期计算 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dev-task/DevTaskDetailDrawer.tsx | 18 ++++++++++-------- apps/web/components/dev-task/DevTaskRow.tsx | 6 +++--- apps/web/lib/dev-task.ts | 16 ++++++++++++++++ apps/web/stores/useDevTaskStore.ts | 6 ++++-- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/apps/web/components/dev-task/DevTaskDetailDrawer.tsx b/apps/web/components/dev-task/DevTaskDetailDrawer.tsx index ace120d..fba20da 100644 --- a/apps/web/components/dev-task/DevTaskDetailDrawer.tsx +++ b/apps/web/components/dev-task/DevTaskDetailDrawer.tsx @@ -1,14 +1,13 @@ 'use client'; import { useState } from 'react'; -import { X, AlertTriangle, Link2, ChevronRight, Clock, User, Tag, Calendar } from 'lucide-react'; +import { X, AlertTriangle, Link2, ChevronRight, Clock, User, Tag, Calendar, Play } from 'lucide-react'; import { StatusBadge } from './StatusBadge'; import { CategoryChip } from './CategoryChip'; -import { WorklogPanel } from './WorklogPanel'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; import { useTaskCategoryStore } from '@/stores/useTaskCategoryStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; -import { ALLOWED_TRANSITIONS, DEV_TASK_STATUS_LABEL, DEV_TASK_STATUS_COLOR, formatHours } from '@/lib/dev-task'; +import { ALLOWED_TRANSITIONS, DEV_TASK_STATUS_LABEL, DEV_TASK_STATUS_COLOR, formatHours, calcActualHoursByDates } from '@/lib/dev-task'; import type { DevTaskStatus } from '@/lib/dev-task'; interface Props { @@ -153,8 +152,15 @@ export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose }: Props) {
实际 - {task.actualHours > 0 ? `${task.actualHours}h` : '-'} + {task.startDate ? formatHours(calcActualHoursByDates(task.startDate, task.completedAt)) : '-'}
+ {task.startDate && ( +
+ + 开始开发 + {task.startDate} +
+ )} {task.dueDate && (
@@ -194,10 +200,6 @@ export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose }: Props) {
)} - {/* 卡片5:工时记录 */} -
- -
diff --git a/apps/web/components/dev-task/DevTaskRow.tsx b/apps/web/components/dev-task/DevTaskRow.tsx index 1006000..079769e 100644 --- a/apps/web/components/dev-task/DevTaskRow.tsx +++ b/apps/web/components/dev-task/DevTaskRow.tsx @@ -3,7 +3,7 @@ import { AlertTriangle } from 'lucide-react'; import { StatusBadge } from './StatusBadge'; import { CategoryChip } from './CategoryChip'; -import { formatHours } from '@/lib/dev-task'; +import { formatHours, calcActualHoursByDates } from '@/lib/dev-task'; import type { DevTask } from '@/lib/dev-task'; import type { TaskCategory } from '@/lib/task-category'; @@ -39,8 +39,8 @@ export function DevTaskRow({ task, category, onClick }: Props) { - {task.actualHours > 0 && {task.actualHours}h} - {task.actualHours > 0 && ' / '} + {task.startDate && {calcActualHoursByDates(task.startDate, task.completedAt)}h} + {task.startDate && ' / '} {formatHours(task.estimateHours).split('(')[0]} {task.assigneeId} diff --git a/apps/web/lib/dev-task.ts b/apps/web/lib/dev-task.ts index 729f285..eabe5f7 100644 --- a/apps/web/lib/dev-task.ts +++ b/apps/web/lib/dev-task.ts @@ -85,6 +85,22 @@ export function formatHours(hours: number): string { return `${hours}h(≈${days}人天)`; } +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; +} + export function generateTaskNo(existingTasks: DevTask[]): string { const maxNum = existingTasks.reduce((max, t) => { const num = parseInt(t.taskNo.replace('DEV-', ''), 10); diff --git a/apps/web/stores/useDevTaskStore.ts b/apps/web/stores/useDevTaskStore.ts index 0488532..2c8d8db 100644 --- a/apps/web/stores/useDevTaskStore.ts +++ b/apps/web/stores/useDevTaskStore.ts @@ -77,8 +77,10 @@ export const useDevTaskStore = create((set, get) => ({ if (!canTransition(task.status, to)) { return { ok: false, message: `不允许从「${task.status}」流转到「${to}」` }; } - const completedAt = to === 'done' ? new Date().toISOString().slice(0, 10) : task.completedAt; - get().updateTask(id, { status: to, completedAt }); + const today = new Date().toISOString().slice(0, 10); + const startDate = (to === 'in_progress' && !task.startDate) ? today : task.startDate; + const completedAt = to === 'done' ? today : task.completedAt; + get().updateTask(id, { status: to, startDate, completedAt }); return { ok: true }; },