Files
ftb-project-management/apps/web/components/dev-task/DevTaskRow.tsx
Script Generator a560634091 feat(dev-task): 开发任务模块 V1 完整实现
- 核心库:dev-task.ts(类型+状态机+进度计算)、task-worklog.ts(工时)、task-category.ts(字典)、work-item.ts(聚合契约)
- Store:useDevTaskStore(CRUD+状态流转)、useTaskWorklogStore(工时记录)、useTaskCategoryStore(字典管理)
- UI组件:DevTaskTab、DevTaskCreateModal、DevTaskDetailDrawer、DevTaskRow、WorklogPanel、StatusBadge、CategoryChip
- 集成:版本详情页"开发任务"Tab、"与我相关"工作台开发任务分组、任务类型管理页
- 设计文档:spec + 实施计划

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-11 18:02:07 +08:00

48 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { AlertTriangle } from 'lucide-react';
import { StatusBadge } from './StatusBadge';
import { CategoryChip } from './CategoryChip';
import { formatHours } from '@/lib/dev-task';
import type { DevTask } from '@/lib/dev-task';
import type { TaskCategory } from '@/lib/task-category';
interface Props {
task: DevTask;
category?: TaskCategory;
onClick?: () => void;
}
const PRIORITY_DOT: Record<string, string> = {
P0: 'bg-red-500',
P1: 'bg-orange-400',
P2: 'bg-blue-400',
P3: 'bg-zinc-300',
};
export function DevTaskRow({ task, category, onClick }: Props) {
return (
<div
onClick={onClick}
className="flex items-center gap-3 px-4 py-2.5 border-b border-[var(--line)] hover:bg-[var(--bg-subtle)] cursor-pointer transition-colors last:border-b-0"
>
<span className={`h-2 w-2 rounded-full shrink-0 ${PRIORITY_DOT[task.priority] || 'bg-zinc-300'}`} title={task.priority} />
<span className="text-[11px] font-mono text-[var(--ink-muted)] w-16 shrink-0">{task.taskNo}</span>
<div className="flex-1 min-w-0 flex items-center gap-1.5">
<span className="text-[13px] text-[var(--ink)] truncate">{task.title}</span>
{task.isBlocked && (
<span className="flex items-center gap-0.5 text-[10px] text-red-500 bg-red-50 px-1.5 py-0.5 rounded shrink-0" title={task.blockReason}>
<AlertTriangle className="h-2.5 w-2.5" />
</span>
)}
</div>
<CategoryChip category={category} />
<StatusBadge status={task.status} />
<span className="text-[11px] text-[var(--ink-muted)] w-20 text-right tabular-nums">
{task.actualHours > 0 ? `${task.actualHours}h/` : ''}{formatHours(task.estimateHours).split('')[0]}
</span>
<span className="text-[11px] text-[var(--ink-soft)] w-14 text-right truncate">{task.assigneeId}</span>
</div>
);
}