fix(version): 修正概览耗时与排名统计

This commit is contained in:
Script Generator
2026-06-26 13:06:22 +08:00
parent 4cda624dc7
commit dde7a9a623
9 changed files with 857 additions and 367 deletions

View File

@@ -1,43 +1,60 @@
import type { Stage } from '@/lib/stage';
import { STAGES } from '@/lib/stage';
import { formatActualDuration } from '@/lib/work-hours';
export interface StageProgressItem {
percent: number;
status: 'idle' | 'active' | 'done';
actualHours?: number;
estimateHours?: number;
aiEstimateHours?: number;
showEstimates?: boolean;
}
export function CapsuleStages({ stageProgress }: {
stageProgress?: Partial<Record<Stage, StageProgressItem>>;
}) {
return (
<div className="flex rounded-lg border border-[var(--line)] overflow-hidden bg-[var(--bg-card)]">
{STAGES.map((stage, idx) => {
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-6">
{STAGES.map((stage) => {
const info = stageProgress?.[stage.key];
const status = info?.status || 'idle';
const percent = info?.percent ?? 0;
const actualHours = info?.actualHours ?? 0;
const stageLabel = stage.key === 'dev' ? '开发任务' : stage.label.replace(' ', '');
const statusLabel = status === 'done' ? '已完成' : status === 'active' ? '进行中' : '未开始';
const tone =
status === 'done'
? 'border-emerald-200 bg-emerald-50/40'
: status === 'active'
? 'border-blue-200 bg-blue-50/40'
: 'border-[var(--line)] bg-[var(--bg-card)]';
const progressClass = status === 'done' ? 'bg-emerald-500' : status === 'active' ? 'bg-blue-500' : 'bg-zinc-300';
return (
<div
key={stage.key}
className={`flex-1 flex flex-col ${idx < STAGES.length - 1 ? 'border-r border-[var(--line-soft)]' : ''}`}
className={`min-h-[148px] rounded-lg border p-3 ${tone}`}
>
<div className="flex items-center justify-between px-2 py-1.5 min-h-[28px]">
<span className={`text-[10px] font-medium leading-tight ${status === 'active' ? 'text-[var(--ink)]' : status === 'done' ? 'text-[var(--ink-soft)]' : 'text-[var(--ink-muted)]'}`}>
{stage.label}
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="truncate text-[12px] font-semibold text-[var(--ink)]">{stageLabel}</div>
<div className="mt-0.5 text-[10px] text-[var(--ink-muted)]">{statusLabel}</div>
</div>
<span className={`shrink-0 rounded-full px-2 py-0.5 text-[11px] font-semibold tabular-nums ${status === 'done' ? 'bg-emerald-100 text-emerald-700' : status === 'active' ? 'bg-blue-100 text-blue-700' : 'bg-zinc-100 text-zinc-500'}`}>
{percent}%
</span>
{status !== 'idle' && (
<span className={`text-[10px] font-medium leading-tight tabular-nums ${status === 'done' ? 'text-emerald-600' : 'text-blue-600'}`}>
{percent}%
</span>
)}
</div>
<div className="h-[3px] w-full bg-zinc-50">
{status === 'done' && <div className="h-full bg-emerald-500 w-full" />}
{status === 'active' && (
<div className="h-full bg-blue-100 w-full">
<div className="h-full bg-blue-500 transition-all" style={{ width: `${percent}%` }} />
</div>
)}
<div className="mt-3 h-1.5 w-full overflow-hidden rounded-full bg-white/80">
<div className={`h-full rounded-full transition-all ${progressClass}`} style={{ width: `${status === 'done' ? 100 : percent}%` }} />
</div>
<div className="mt-3 grid gap-2 text-[11px]">
<div className="flex items-center justify-between gap-2">
<span className="text-[var(--ink-muted)]"></span>
<span className="font-medium tabular-nums text-[var(--ink)]">{formatActualDuration(actualHours)}</span>
</div>
{info?.showEstimates && <EstimateRow label="执行预估" hours={info.estimateHours} />}
{info?.showEstimates && <EstimateRow label="AI预估" hours={info.aiEstimateHours} accent />}
</div>
</div>
);
@@ -45,3 +62,14 @@ export function CapsuleStages({ stageProgress }: {
</div>
);
}
function EstimateRow({ label, hours, accent }: { label: string; hours?: number; accent?: boolean }) {
return (
<div className="flex items-center justify-between gap-2">
<span className="text-[var(--ink-muted)]">{label}</span>
<span className={`font-medium tabular-nums ${accent ? 'text-violet-600' : 'text-[var(--ink)]'}`}>
{hours !== undefined ? formatActualDuration(hours) : '-'}
</span>
</div>
);
}