'use client'; import { useState } from 'react'; import { X, AlertTriangle, Link2, ChevronRight, Clock, User, Tag, Calendar, Play, Trash2, Pencil } from 'lucide-react'; import { StatusBadge } from './StatusBadge'; import { CategoryChip } from './CategoryChip'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; import { useTaskCategoryStore } from '@/stores/useTaskCategoryStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { ALLOWED_TRANSITIONS, DEV_TASK_STATUS_LABEL, DEV_TASK_STATUS_COLOR, formatHours, calcActualHoursByDates } from '@/lib/dev-task'; import type { DevTaskStatus } from '@/lib/dev-task'; interface Props { taskId: string; allTaskIds: string[]; onClose: () => void; } export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose }: Props) { const { tasks, changeStatus, setBlocked, deleteTask } = useDevTaskStore(); const { categories } = useTaskCategoryStore(); const { requirements } = useRequirementStore(); const task = tasks.find((t) => t.id === taskId); if (!task) return null; const category = categories.find((c) => c.id === task.categoryId); const requirement = requirements.find((r) => r.id === task.requirementId); const allTasks = tasks.filter((t) => allTaskIds.includes(t.id)); const predecessors = (task.predecessorIds || []).map((id) => allTasks.find((t) => t.id === id)).filter(Boolean); const nextStatuses = ALLOWED_TRANSITIONS[task.status] || []; const [blockReason, setBlockReason] = useState(task.blockReason || ''); const [showBlockInput, setShowBlockInput] = useState(false); const handleTransition = (to: DevTaskStatus) => { const result = changeStatus(task.id, to); if (!result.ok) alert(result.message); }; const handleBlock = () => { if (!blockReason.trim()) return; setBlocked(task.id, true, blockReason.trim()); setShowBlockInput(false); }; const handleUnblock = () => { setBlocked(task.id, false); setBlockReason(''); }; return (
e.stopPropagation()}> {/* 顶栏 */}
{task.taskNo} {task.title}
{/* 滚动区域 */}
{/* 卡片1:关联需求 */} {requirement && (
关联需求
{requirement.code} {requirement.title}
)} {/* 卡片2:状态 & 操作 */}
状态 & 操作
{DEV_TASK_STATUS_LABEL[task.status]} {task.isBlocked && ( 阻塞中 )}
{/* 流转按钮 */} {nextStatuses.length > 0 && (
{nextStatuses.map((s) => ( ))}
)} {/* 阻塞管理 */}
{task.isBlocked ? (
{task.blockReason}
) : ( showBlockInput ? (
setBlockReason(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleBlock(); }} placeholder="阻塞原因" className="flex-1 h-8 rounded-lg border border-[var(--line)] px-3 text-[12px] focus:border-red-400 focus:outline-none" autoFocus />
) : ( ) )}
{/* 卡片3:基本信息 */}
基本信息
类型
负责人 {task.assigneeId}
优先级 {task.priority}
预计 {formatHours(task.estimateHours)}
实际 {task.startDate ? formatHours(calcActualHoursByDates(task.startDate, task.completedAt)) : '-'}
{task.startDate && (
开始开发 {task.startDate.slice(0, 16).replace('T', ' ')}
)} {task.dueDate && (
截止 {task.dueDate}
)} {task.completedAt && (
提测 {task.completedAt.slice(0, 16).replace('T', ' ')}
)}
{task.description && (

{task.description}

)}
{/* 卡片4:前置任务 */} {predecessors.length > 0 && (
前置任务
{predecessors.map((p: any) => (
{p.taskNo} {p.title}
))}
)}
); }