fix: 调研/产品/UI 改为手动点击"开始"触发计时

去掉"到了计划日期自动变进行中"的逻辑,改为用户手动点击:
- pending 状态显示蓝色"开始"按钮
- 点击后记录 actualStartAt(精确时间戳)
- 进行中才显示"完成"按钮

跟开发/测试逻辑一致:状态由用户主动触发,时间戳准确。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Script Generator
2026-06-12 16:38:03 +08:00
parent 7af9206bec
commit feba142ee9

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useState } from 'react'; import { useState } from 'react';
import { Plus, Pencil, Trash2, X, Check, ExternalLink, FileUp, Link2 } from 'lucide-react'; import { Plus, Pencil, Trash2, X, Check, ExternalLink, FileUp, Link2, Play } from 'lucide-react';
import type { VersionPlan, PlanTask } from '@/lib/version-plan'; import type { VersionPlan, PlanTask } from '@/lib/version-plan';
import { calcPlanDuration, formatDuration, calcTotalDuration, calcPlanProgress, calcLinkedReqProgress } from '@/lib/version-plan'; import { calcPlanDuration, formatDuration, calcTotalDuration, calcPlanProgress, calcLinkedReqProgress } from '@/lib/version-plan';
@@ -56,19 +56,11 @@ export function PlanTab({ plans, versionId, versionDeadline, currentUserName, pl
<div className="space-y-3"> <div className="space-y-3">
{typePlans.map((plan) => { {typePlans.map((plan) => {
const now = new Date().toISOString(); const now = new Date().toISOString();
const nowDate = now.slice(0, 10);
// 自动进入进行中(到了计划开始日期)
const shouldAutoStart = plan.status === 'pending' && plan.startTime <= nowDate;
if (shouldAutoStart && !plan.actualStartAt) {
onUpdate(plan.id, { status: 'in_progress' });
}
const effectiveStatus = shouldAutoStart ? 'in_progress' : plan.status;
// 耗时用实际时间戳计算 // 耗时用实际时间戳计算
const actualStart = plan.actualStartAt || (shouldAutoStart ? now : null); const dur = plan.status === 'completed' && plan.completedAt && plan.actualStartAt
const dur = effectiveStatus === 'completed' && plan.completedAt && actualStart ? calcPlanDuration(plan.actualStartAt, plan.completedAt)
? calcPlanDuration(actualStart, plan.completedAt) : plan.status === 'in_progress' && plan.actualStartAt
: effectiveStatus === 'in_progress' && actualStart ? calcPlanDuration(plan.actualStartAt, now)
? calcPlanDuration(actualStart, now)
: { days: 0, hours: 0 }; : { days: 0, hours: 0 };
const durText = dur.days > 0 || dur.hours > 0 ? formatDuration(dur.days, dur.hours) : '-'; const durText = dur.days > 0 || dur.hours > 0 ? formatDuration(dur.days, dur.hours) : '-';
return ( return (
@@ -77,8 +69,8 @@ export function PlanTab({ plans, versionId, versionDeadline, currentUserName, pl
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-2 mb-1.5"> <div className="flex items-center gap-2 mb-1.5">
<span className="text-[13px] font-medium text-[var(--ink)]">{plan.title}</span> <span className="text-[13px] font-medium text-[var(--ink)]">{plan.title}</span>
<span className={`inline-flex items-center rounded-md px-2 py-0.5 text-[10px] font-medium border ${STATUS_STYLE[effectiveStatus]}`}> <span className={`inline-flex items-center rounded-md px-2 py-0.5 text-[10px] font-medium border ${STATUS_STYLE[plan.status]}`}>
{STATUS_LABEL[effectiveStatus]} {STATUS_LABEL[plan.status]}
</span> </span>
</div> </div>
<div className="flex items-center gap-4 text-[12px] text-[var(--ink-soft)]"> <div className="flex items-center gap-4 text-[12px] text-[var(--ink-soft)]">
@@ -164,7 +156,7 @@ export function PlanTab({ plans, versionId, versionDeadline, currentUserName, pl
) : null; ) : null;
})} })}
</div> </div>
{calcLinkedReqProgress(plan.linkedRequirementIds, plan.completedRequirementIds) === 100 && effectiveStatus !== 'completed' && ( {calcLinkedReqProgress(plan.linkedRequirementIds, plan.completedRequirementIds) === 100 && plan.status !== 'completed' && (
<div className="mt-2 rounded-lg bg-green-50 border border-green-200 px-3 py-2 flex items-center justify-between"> <div className="mt-2 rounded-lg bg-green-50 border border-green-200 px-3 py-2 flex items-center justify-between">
<span className="text-[12px] text-green-700"></span> <span className="text-[12px] text-green-700"></span>
<button onClick={() => setCompletingPlan(plan)} className="text-[11px] font-medium text-green-700 hover:text-green-900 underline"></button> <button onClick={() => setCompletingPlan(plan)} className="text-[11px] font-medium text-green-700 hover:text-green-900 underline"></button>
@@ -174,11 +166,18 @@ export function PlanTab({ plans, versionId, versionDeadline, currentUserName, pl
)} )}
</div> </div>
<div className="flex items-center gap-1 ml-3"> <div className="flex items-center gap-1 ml-3">
{effectiveStatus !== 'completed' && ( {plan.status === 'pending' && (
<button onClick={() => onUpdate(plan.id, { status: 'in_progress' })} className="h-7 px-2 flex items-center gap-1 rounded-md text-[11px] font-medium text-blue-600 hover:bg-blue-50 border border-blue-200" title="开始">
<Play className="h-3 w-3" />
</button>
)}
{plan.status !== 'completed' && plan.status !== 'pending' && (
<button onClick={() => setCompletingPlan(plan)} className="h-7 w-7 flex items-center justify-center rounded-md text-green-600 hover:bg-green-50" title="标记完成">
<Check className="h-3.5 w-3.5" />
</button>
)}
{plan.status !== 'completed' && (
<> <>
<button onClick={() => setCompletingPlan(plan)} className="h-7 w-7 flex items-center justify-center rounded-md text-green-600 hover:bg-green-50" title="标记完成">
<Check className="h-3.5 w-3.5" />
</button>
<button onClick={() => setEditingPlan(plan)} className="h-7 w-7 flex items-center justify-center rounded-md text-[var(--ink-muted)] hover:bg-[var(--bg-subtle)]" title="编辑"> <button onClick={() => setEditingPlan(plan)} className="h-7 w-7 flex items-center justify-center rounded-md text-[var(--ink-muted)] hover:bg-[var(--bg-subtle)]" title="编辑">
<Pencil className="h-3.5 w-3.5" /> <Pencil className="h-3.5 w-3.5" />
</button> </button>