From da1f98968bc05ebf3802921dde1bfd488d3da2aa Mon Sep 17 00:00:00 2001 From: Script Generator Date: Fri, 12 Jun 2026 16:24:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=B0=83=E7=A0=94/=E4=BA=A7=E5=93=81/UI?= =?UTF-8?q?=E9=98=B6=E6=AE=B5=E8=80=97=E6=97=B6=E6=94=B9=E4=B8=BA=E7=B2=BE?= =?UTF-8?q?=E7=A1=AE=E6=97=B6=E9=97=B4=E6=88=B3=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VersionPlan 新增 actualStartAt 字段 - updatePlan 流转到 in_progress 时自动记录 actualStartAt - updatePlan 流转到 completed 时自动记录 completedAt(如未设) - 概览阶段耗时改用 actualStartAt/completedAt 计算(不再用计划排期) - 个人耗时汇总同步使用实际时间戳 所有阶段(调研/产品/UI/开发/测试)现在统一用精确时间戳算耗时。 Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web/app/versions/[id]/page.tsx | 20 ++++++++++++++------ apps/web/lib/version-plan.ts | 1 + apps/web/stores/useVersionPlanStore.ts | 13 ++++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/apps/web/app/versions/[id]/page.tsx b/apps/web/app/versions/[id]/page.tsx index bf09f4f..0a26db7 100644 --- a/apps/web/app/versions/[id]/page.tsx +++ b/apps/web/app/versions/[id]/page.tsx @@ -393,15 +393,24 @@ export default function VersionDetailPage() { // 调研阶段 const researchPlans = versionPlans.filter((p) => p.type === 'research'); - const researchCal = calcCalendar(researchPlans.map((p) => p.startTime), researchPlans.filter((p) => p.status === 'completed').map((p) => p.endTime)); + const researchCal = calcCalendar( + researchPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!), + researchPlans.filter((p) => p.completedAt).map((p) => p.completedAt!), + ); // 产品方案阶段 const productPlans = versionPlans.filter((p) => p.type === 'product'); - const productCal = calcCalendar(productPlans.map((p) => p.startTime), productPlans.filter((p) => p.status === 'completed').map((p) => p.endTime)); + const productCal = calcCalendar( + productPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!), + productPlans.filter((p) => p.completedAt).map((p) => p.completedAt!), + ); // UI设计阶段 const uiPlans = versionPlans.filter((p) => p.type === 'ui'); - const uiCal = calcCalendar(uiPlans.map((p) => p.startTime), uiPlans.filter((p) => p.status === 'completed').map((p) => p.endTime)); + const uiCal = calcCalendar( + uiPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!), + uiPlans.filter((p) => p.completedAt).map((p) => p.completedAt!), + ); // 开发阶段 const devCal = calcCalendar( @@ -434,9 +443,8 @@ export default function VersionDetailPage() { // 调研/产品/UI 用 startTime→endTime 计算 versionPlans.forEach((p) => { - if (!p.owner || !p.startTime) return; - const end = p.status === 'completed' ? p.endTime : undefined; - const hours = calcActualHoursByDates(p.startTime.slice(0, 10), end?.slice(0, 10)); + if (!p.owner || !p.actualStartAt) return; + const hours = calcActualHoursByDates(p.actualStartAt, p.completedAt); addHours(p.owner, p.type === 'research' ? 'research' : p.type === 'product' ? 'product' : 'ui', hours); }); versionDevTasks.forEach((t) => { diff --git a/apps/web/lib/version-plan.ts b/apps/web/lib/version-plan.ts index d18dea3..b64b809 100644 --- a/apps/web/lib/version-plan.ts +++ b/apps/web/lib/version-plan.ts @@ -24,6 +24,7 @@ export interface VersionPlan { resultFileData?: string; remark?: string; overdueReason?: string; + actualStartAt?: string; createdAt: string; completedAt?: string; addedBy: string; diff --git a/apps/web/stores/useVersionPlanStore.ts b/apps/web/stores/useVersionPlanStore.ts index 2f757c7..891b26a 100644 --- a/apps/web/stores/useVersionPlanStore.ts +++ b/apps/web/stores/useVersionPlanStore.ts @@ -43,7 +43,18 @@ export const useVersionPlanStore = create((set, get) => ({ }, updatePlan: (id, data) => { - const plans = get().plans.map((p) => p.id === id ? { ...p, ...data } : p); + const now = new Date().toISOString(); + const plans = get().plans.map((p) => { + if (p.id !== id) return p; + const patch = { ...data }; + if (patch.status === 'in_progress' && !p.actualStartAt) { + (patch as any).actualStartAt = now; + } + if (patch.status === 'completed' && !p.completedAt) { + (patch as any).completedAt = now; + } + return { ...p, ...patch }; + }); set({ plans }); saveLocal(plans); },