fix(version): 修正概览耗时与排名统计
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user